分割線內容轉載自http://hzwer.com/879.html
---------------------------------------------------------------------------------
第一次做線段樹掃描法的題,網搜各種講解,發現大多數都講得太過簡潔,不是太容易理解。所以自己打算寫一個詳細的。看完必會o(∩_∩)o
顧名思義,掃描法就是用一根想象中的線掃過所有矩形,在寫代碼的過程中,這根線很重要。方向的話,可以左右掃,也可以上下掃。方法是一樣的,這里我用的是由下向上的掃描法。
如上圖所示,坐標系內有兩個矩形。位置分別由左下角和右上角頂點的坐標來給出。上下掃描法是對x軸建立線段樹,矩形與y平行的兩條邊是沒有用的,在這里直接去掉。如下圖。
現想象有一條線從最下面的邊開始依次向上掃描。線段樹用來維護當前覆蓋在x軸上的線段的總長度,初始時總長度為0。用ret來保存矩形面積總和,初始時為0。
由下往上掃描,掃描到矩形的底邊時將它插入線段樹,掃描到矩形的頂邊時將底邊從線段樹中刪除。而在代碼中實現的方法就是,每條邊都有一個flag變量,底邊為1,頂邊為-1。
用cover數組(通過線段樹維護)來表示某x軸坐標區間內是否有邊覆蓋,初始時全部為0。插入或刪除操作直接讓cover[] += flag。當cover[] > 0 時,該區間一定有邊覆蓋。
開始掃描到第一條線,將它壓入線段樹,此時覆蓋在x軸上的線段的總長度L為10。計算一下它與下一條將被掃描到的邊的距離S(即兩條線段的縱坐標之差,該例子里此時為3)。
則 ret += L * S. (例子里增量為10*3=30)
結果如下圖
橙色區域表示已經計算出的面積。
掃描到第二條邊,將它壓入線段樹,計算出此時覆蓋在x軸上的邊的總長度。
例子里此時L=15。與下一條將被掃描到的邊的距離S=2。 ret += 30。 如下圖所示。
綠色區域為第二次面積的增量。
接下來掃描到了下方矩形的頂邊,從線段樹中刪除該矩形的底邊,並計算接下來面積的增量。如下圖。
藍色區域為面積的增量。
此時矩形覆蓋的總面積已經計算完成。 可以看到,當共有n條底邊和頂邊時,只需要從下往上掃描n-1條邊即可計算出總面積。
此題因為橫坐標包含浮點數,因此先離散化。另外,因為用線段樹維護的是覆蓋在x軸上的邊,而邊是連續的,並非是一個個斷點,因此線段樹的每一個葉子結點實際存儲的是該點與下一點之間的距離。
12.25
---------------------------------------------------------------------------------
這道題我一直在糾結,怎么求當前有掃描線上有的線段總長?怎么lazy下放?我一直想的是每個點維護的都是它維護的這個區間內的總的cnt等等。
后來我發現換個思路,一切都很簡單!
我的每個節點t[x].l~t[x].r維護的其實是線段t[x].l~(t[x].r+1),也就是若干條線段,因為點分成左右孩子的時候會有問題(比如[3,3]維護的到底是什么?)。
我們要把每個節點看成是一條線段。
對於每個節點維護兩個值:
cnt:這個點所代表的線段被覆蓋了多少次。
len:以這個點為根的子樹中被覆蓋的區間一共有多長。
當一條線段進來的時候,在代表它的那若干個節點上cnt++,其它節點cnt不用加。
然后len維護的就是這個區間內那些cnt>0的節點所覆蓋的區間總長。
我做慣了葉子節點才有實際意義的線段樹,思路太過狹隘,被卡了這么久,其實線段樹上每個節點都可以有它的實際意義。
1 #include<cstdio>
2 #include<cstdlib>
3 #include<cstring>
4 #include<cmath>
5 #include<iostream>
6 #include<algorithm>
7 using namespace std; 8
9 const int N=1100,INF=(int)1e9; 10 double z[N][4]; 11 struct point{ 12 double d; 13 int x,y; 14 }p[2*N]; 15 struct node{ 16 int x0,x1,d; 17 double y; 18 }a[N]; 19 struct trnode{ 20 int l,r,lc,rc,cnt; 21 double rl,len; 22 }t[2*N]; 23 double num[N]; 24 int n,tl,pl,al; 25
26 int minn(int x,int y){return x<y ? x:y;} 27 int maxx(int x,int y){return x>y ? x:y;} 28 bool cmp_d(point x,point y){return x.d<y.d;} 29 bool cmp_y(node x,node y){return x.y<y.y;} 30
31 int bt(int l,int r) 32 { 33 int x=++tl; 34 t[x].l=l;t[x].r=r; 35 t[x].lc=t[x].rc=0; 36 t[x].cnt=0;t[x].len=0; 37 t[x].rl=num[r+1]-num[l]; 38 if(l<r) 39 { 40 int mid=(l+r)/2; 41 t[x].lc=bt(l,mid); 42 t[x].rc=bt(mid+1,r); 43 } 44 return x; 45 } 46
47 void upd(int x) 48 { 49 int lc=t[x].lc,rc=t[x].rc; 50 if(t[x].cnt>0) t[x].len=t[x].rl; 51 else t[x].len=t[lc].len+t[rc].len; 52 } 53
54 void change(int x,int l,int r,int d) 55 { 56 if(t[x].l==l && t[x].r==r) 57 { 58 t[x].cnt+=d; 59 upd(x); 60 return ; 61 } 62 int lc=t[x].lc,rc=t[x].rc,mid=(t[x].l+t[x].r)/2; 63 if(r<=mid) change(lc,l,r,d); 64 else if(l>mid) change(rc,l,r,d); 65 else
66 { 67 change(lc,l,mid,d); 68 change(rc,mid+1,r,d); 69 } 70 upd(x); 71 } 72
73 int main() 74 { 75 freopen("a.in","r",stdin); 76 int T=0; 77 while(1) 78 { 79 scanf("%d",&n); 80 if(n==0) break; 81 int x,mx;pl=0;tl=0;al=0;t[0].len=0; 82 for(int i=1;i<=n;i++) 83 { 84 for(int j=0;j<=3;j++) 85 { 86 scanf("%lf",&z[i][j]); 87 if(j%2==0) p[++pl].d=z[i][j],p[pl].x=i,p[pl].y=j; 88 } 89 } 90 sort(p+1,p+1+pl,cmp_d); 91 mx=0;p[0].d=INF; 92 for(int i=1;i<=pl;i++) 93 { 94 if(p[i].d!=p[i-1].d) mx++,num[mx]=p[i].d; 95 z[p[i].x][p[i].y]=mx; 96 } 97 bt(1,mx); 98 for(int i=1;i<=n;i++) 99 { 100 if(z[i][1]<z[i][3]) swap(z[i][1],z[i][3]); 101 if(z[i][0]>z[i][2]) swap(z[i][0],z[i][2]); 102 a[++al].x0=z[i][0];a[al].x1=z[i][2];a[al].y=z[i][1];a[al].d=-1; 103 a[++al].x0=z[i][0];a[al].x1=z[i][2];a[al].y=z[i][3];a[al].d=1; 104 } 105 sort(a+1,a+1+al,cmp_y); 106 // for(int i=1;i<=al;i++) 107 // printf("%d -> %d %lf %d\n",a[i].x0,a[i].x1,a[i].y,a[i].d);
108 double w,h,ans=0; 109 change(1,a[1].x0,a[1].x1-1,a[1].d); 110 for(int i=2;i<=al;i++) 111 { 112 h=a[i].y-a[i-1].y; 113 w=t[1].len; 114 ans+=w*h; 115 // printf("w = %lf h = %lf\n",w,h);
116 change(1,a[i].x0,a[i].x1-1,a[i].d); 117 } 118 printf("Test case #%d\nTotal explored area: %.2lf\n\n",++T,ans); 119 } 120 return 0; 121 }