線段樹應用:掃描線
阿新 • • 發佈:2018-11-25
掃描線暴力解決的話時間和空間複雜度往往是不夠的。
所以,掃描線也就成了線段樹很大的應用。
具體原理解釋(寫得很好):
https://blog.csdn.net/u013480600/article/details/22548393
https://blog.csdn.net/zearot/article/details/48299459#t19
https://www.cnblogs.com/scau20110726/archive/2013/04/12/3016765.html(圖片很好)
給出模板:第一篇部落格(饒齊大佬)的修正與改進,還有一些地方的解釋,錯誤原因
第一個:
我們線上段樹更新的時候,是這樣往下走的:
sumv[O]=sumv[O*2]+sumv[O*2+1].
如果左子樹維護的區間[l,mid],
右子樹維護的區間[mid+1,r]
那麼還有一個區間[mid,mid+1]沒有統計到,怎麼辦?
下面程式碼解釋是左閉右開,請讀者自行思考。
/* 我們線上段樹更新的時候,是這樣往下走的: sumv[O]=sumv[O*2]+sumv[O*2+1]. 如果左子樹維護的區間[l,mid], 右子樹維護的區間[mid+1,r] 那麼還有一個區間[mid,mid+1]沒有統計到,怎麼辦? 請讀者自行思考。*/ #include<bits/stdc++.h> using namespace std; const int MAXN=2222; #define lson i<<1,l,m #define rson i<<1|1,m+1,r #define root 1,1,k-1 double X[MAXN]; struct node { double l,r,h; int d; ///d為1或-1,標記掃描線是矩形的上位還是下位邊. node(){} node(double a,double b,double c,int d): l(a),r(b),h(c),d(d){} bool operator <(const node &b)const { return h<b.h; } }nodes[MAXN]; ///cnt: >=0時表示本節點控制的區域內下位邊個數-上位邊個數的結果. ///如果==-1時,表示本節點左右子樹的上下位邊數不一致. ///sum: 本節點控制的區域內cnt值不為0的區域總長度. int cnt[MAXN*4]; double sum[MAXN*4]; ///如果cnt!=-1,那麼下放cnt資訊,並更新子節點的sum資訊. void PushDown(int i,int l,int r) { int m=(l+r)>>1; if(cnt[i]!=-1) { cnt[i<<1]=cnt[i<<1|1]=cnt[i]; sum[i<<1]= (cnt[i]?(X[m+1]-X[l]):0) ; sum[i<<1|1]= (cnt[i]?(X[r+1]-X[m+1]):0) ; } } ///根據子節點的cnt值和sum值更新父節點的cnt和sum值 void PushUp(int i,int l,int r) { if(cnt[i<<1]==-1 || cnt[i<<1|1]==-1) cnt[i]=-1; else if(cnt[i<<1] != cnt[i<<1|1]) cnt[i]=-1; else cnt[i]=cnt[i<<1]; sum[i]=sum[i<<1]+sum[i<<1|1]; } void update(int ql,int qr,int v,int i,int l,int r) { if(ql<=l && r<=qr) { if(cnt[i]!=-1) { cnt[i]+=v; sum[i] = (cnt[i]? (X[r+1]-X[l]):0); return ; } } PushDown(i,l,r); int m=(l+r)>>1; if(ql<=m) update(ql,qr,v,lson); if(m<qr) update(ql,qr,v,rson); PushUp(i,l,r); } ///二分尋找區間 ,每一個掃描線 int bin(double key,int n,double d[]) { int l=1,r=n; while(r>=l) { int m=(r+l)>>1; if(d[m]==key) return m; else if(d[m]>key) r=m-1; else l=m+1; } return -1; } int main() { int q; int kase=0; while(scanf("%d",&q)==1&&q) { int n=0,m=0; for(int i=1;i<=q;i++) { double x1,y1,x2,y2; scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); X[++n]=x1; nodes[++m]=node(x1,x2,y1,1); X[++n]=x2; nodes[++m]=node(x1,x2,y2,-1); } sort(X+1,X+n+1); sort(nodes+1,nodes+m+1); int k=unique(X+1,X+n+1)-(X+1);//去掉相鄰的重複元素 //build(1,1,k-1); memset(sum,0,sizeof(sum)); memset(cnt,0,sizeof(cnt)); double ret=0.0;//最終面積 for(int i=1;i<m;i++) { int l=bin(nodes[i].l,k,X); int r=bin(nodes[i].r,k,X)-1;///左開右閉的性質 //cout<<l<<" "<<r<<endl; if(l<=r) update(l,r,nodes[i].d,root); ret += sum[1]*(nodes[i+1].h-nodes[i].h); } printf("Test case #%d\nTotal explored area: %.2lf\n\n",++kase,ret ); } }
經典例題: