Gene_I must wait for the sunrise
題意:
本題是掃描線的典型應用,就是求矩形的面積交。與本題相關的題就是HDU1542,求矩形面積並。
思路:
我們先來討論一下掃描線的本質。掃描線演算法其實就是先將所有的 y 座標進行離散化,然後用所有離散的點建立一棵線段樹,這棵線段樹的每一個節點的左右端點就代表空間中 y 座標上的一段區間。
然後我們再將每一個矩形的左右兩條邊抽出來,進行排序,然後一一存進線段樹中。
每一根線記錄一個flag屬性,左線標記為1,右線標記為-1,然後將每一根線存入線段樹中的時候,就會對線段樹的某一段區間進行覆蓋,然後我們需要記錄每一個區間被覆蓋的次數。然後每一根線段所帶來的面積就是 ans += t[1].len * ( line[i].x - line[i-1].x ),由此便可以求出所有被覆蓋過一次的面積。
那麼本題就是掃描線模板的一個變種。
我們依舊記錄每一個線段樹節點被完全覆蓋的次數,而且當線段樹中某一個節點被覆蓋了一次,那麼該節點的兒子不會被標記為覆蓋,所以區間的覆蓋都是獨立的。
然後我們再記錄一下每個節點被覆蓋一次以上的長度,和兩次以上的長度。
那麼對於calc()函式,
當這段區間被覆蓋了兩次以上,則該區間的len2 = len1 = t[p].mr-t[p].ml;
當這段區間被覆蓋了一次,則先判斷這是不是葉節點,如果不是葉節點,則len2 = t[p*2].len1+t[p*2+1].len1,此處就是本題的關鍵不同之處,其餘條件語句詳見程式碼。
總結:
對於掃描線的問題,本質就在於離散化,然後標記每一個區間被覆蓋了多少次,而且每一個區間被覆蓋都是獨立的。然後就是多做掃描線的題了,然後熟練掌握此類問題的解決方法。
程式碼:
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #define rep(i,a,b) for(int i = a; i <= b; i++) using namespace std; const int N = 4000; struct Line{ double x,y1,y2; int flag; }line[N]; int n, num; double y[N], ans; struct Tree{ int l,r,s; double ml,mr,len1,len2; //記錄覆蓋一次以上的長度 和 覆蓋兩次以上的長度 }t[N*4]; bool cmp(Line a,Line b) { return a.x < b.x; } void build(int p,int l,int r) { t[p].l = l, t[p].r = r, t[p].ml = y[l], t[p].mr = y[r], t[p].s = 0, t[p].len1 = 0, t[p].len2 = 0; // cout << t[p].ml << " " << t[p].mr << endl; if(l == (r-1)) return; int mid = (l+r)>>1; build(p*2,l,mid); build(p*2+1,mid,r); } void calc(int p) { if(t[p].s >= 2){ t[p].len1 = t[p].len2 = t[p].mr-t[p].ml; } else if(t[p].s == 1){ //如果這段區間只被完全覆蓋過一次,那麼這段區間被覆蓋兩次的長度為 左右兒子被覆蓋一次的長度之和 //因為我們寫掃描線這題的時候,如果一個區間被完全覆蓋,那麼這段區間被覆蓋的次數+1 //但是我們沒有下傳覆蓋標誌,所以這段區間的左右兒子並沒有被標記覆蓋 //所以每一段區間被覆蓋的次數都是獨立的 if(t[p].r == (t[p].l+1)) t[p].len2 = 0; else t[p].len2 = t[p*2].len1+t[p*2+1].len1; t[p].len1 = t[p].mr-t[p].ml; } else if(t[p].r == (t[p].l+1)){ t[p].len1 = t[p].len2 = 0; } else{ t[p].len1 = t[p*2].len1+t[p*2+1].len1; t[p].len2 = t[p*2].len2+t[p*2+1].len2; } } void change(int p, Line tp) { if(tp.y1 <= t[p].ml && tp.y2 >= t[p].mr) { t[p].s += tp.flag; calc(p); return; } if(t[p*2].mr >= tp.y2) change(p*2,tp); else if(t[p*2+1].ml <= tp.y1) change(p*2+1,tp); else{ change(p*2,tp); change(p*2+1,tp); } calc(p); } int main() { int T; scanf("%d",&T); while(T--) { ans = 0, num = 0; scanf("%d",&n); rep(i,1,n){ double x1,x2,y1,y2; scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); line[++num].x = x1, line[num].y1 = y1, line[num].y2 = y2, line[num].flag = 1; y[num] = y1; line[++num].x = x2, line[num].y1 = y1, line[num].y2 = y2, line[num].flag = -1; y[num] = y2; } sort(line+1,line+1+num,cmp); sort(y+1,y+1+num); int sc = unique(y+1,y+num+1)-y-1; build(1,1,sc); change(1,line[1]); rep(i,2,num) { ans += t[1].len2*(line[i].x-line[i-1].x); change(1,line[i]); } printf("%.2f\n",ans); } return 0; }