ACM-线段树扫描线总结
掃描線的基礎概念可以看這幾篇文章
http://blog.csdn.net/xingyeyongheng/article/details/8927732
http://www.cnblogs.com/scau20110726/archive/2013/03/21/2972808.html
?
在這里講一下應用
?
1.求矩形的面積并
以hdu 1542為例:
給出n個矩陣的對角坐標,求矩陣的面積并。
?
這里需要注意這么幾點:
- 離散化
因為數據跨度很大,所以肯定要將數據離散化,保存在hash數組中。但要小心離散化本身的坑。
離散化的坑,就是在表示線段樹時,我們會把1-4劃分為1-2和3-4,如果覆蓋1-2和3-4就等同于把整個線段覆蓋了,但其實不對,因為2和3中間也是有值的。
?下面這題也遇到這個坑,解決方法是:在update時讀入r-1,計算時使用r+1
- 水平掃描的話,hash的是y坐標,反之亦然
- hash最好要去重
?
#include <iostream> #include <string> #include <cstring> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm> #include <stack> #include <queue> #include <cctype> #include <vector> #include <iterator> #include <set> #include <map> #include <sstream> using namespace std;#define mem(a,b) memset(a,b,sizeof(a)) #define pf printf #define sf scanf #define spf sprintf #define pb push_back #define debug printf("!\n") #define MAXN 200+5 #define MAX(a,b) a>b?a:b #define blank pf("\n") #define LL long long #define ALL(x) x.begin(),x.end() #define INS(x) inserter(x,x.begin()) #define pqueue priority_queue #define INF 0x3f3f3f3f#define ls (rt<<1) #define rs (rt<<1|1)int n,m;double hh[MAXN],col[MAXN<<2],len[MAXN<<2];struct node {double l,r,x,c;node(){}node(double a,double b,double c,double d):l(a),r(b),x(c),c(d){}bool operator < (const node &b) const{return x<b.x;} }a[MAXN];void PushUp(int rt,int l,int r) {if(col[rt]){len[rt] = hh[r+1] - hh[l];}else if(l==r) len[rt] = 0;else{len[rt] = len[ls]+len[rs];} }void update(int val,int L,int R,int l,int r,int rt) {if(L<=l && r<=R){col[rt] += val;PushUp(rt,l,r);return;}int mid = (l+r)>>1;if(L <= mid) update(val,L,R,l,mid,ls);if(R > mid) update(val,L,R,mid+1,r,rs);PushUp(rt,l,r); }int main() {int i,j,k,t,kase=1;while(~sf("%d",&n) && n){int v=0;double sum = 0;for(i=0;i<n;i++){double x1,y1,x2,y2;sf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);hh[v] = y1;a[v++] = node(y1,y2,x1,1);hh[v] = y2;a[v++] = node(y1,y2,x2,-1);}sort(hh,hh+v);sort(a,a+v);int d = 1;for(i=1;i<v;i++){if(hh[i]!=hh[i-1]) hh[d++] = hh[i];}mem(len,0);mem(col,0);for(i=0;i<v-1;i++){int l = lower_bound(hh,hh+d,a[i].l)-hh;int r = lower_bound(hh,hh+d,a[i].r)-hh-1;update(a[i].c,l,r,0,d-1,1);sum+=len[1]*(a[i+1].x-a[i].x);//pf("%lf %lf\n",sum,len[1]); }pf("Test case #%d\nTotal explored area: %.2lf\n\n",kase++,sum);}return 0; }?
2.求矩形周長并
以hdu 1828為例:
http://blog.csdn.net/xingyeyongheng/article/details/8931410
http://www.cnblogs.com/scau20110726/archive/2013/04/13/3018687.html
http://www.cnblogs.com/scau20110726/archive/2013/04/13/3018702.html
三篇很不錯的文章,基礎概念可以看這幾個
?
從面積變成了周長,方法其實是差不多的。解決這個問題有兩種思路:
-
把矩形分成橫線和豎線去處理,可知是完全相同的操作,我們來講下怎么算出橫線部分,豎線部分就是照搬即可。
將橫線保存在一個表中,按橫線所處的豎直位置排序(升序),另外每條橫線帶一個標記值,原矩形的下線為1,上線為-1(對應過去就是插入線段和刪除線段)
從低到高掃描橫線,沒掃到一條橫線就能計算出一部分橫線值。計算方法是算出現在總區間的被覆蓋的長度,然后求出與上一次的總區間的覆蓋長度的差(即相減求絕對值),因為每次添加了一條線段,如果沒有沒有使總區間覆蓋長度發生變化,說明這條線段其實在多邊形的內部,被覆蓋掉了,不能計算,只要能引起總區間長度發生變化的,說明該線段不被覆蓋不被包含,要計算
而豎線部分的做法是一樣的,把豎線保存在一個表中,按水平位置排序(升序),每條橫線帶一個標記值,原矩形的左線為1,右線為-1,然后同樣地操作
-
前面的工作是相同的,把橫線保存在一個表中,按豎直位置排序,然后從下往上掃描所有橫線,在這個方法中,每次掃描一條橫線,都能計算出兩不部分值,一部分是橫線的,一部分是豎線的
而計算橫線部分的方法和第一種方法是一樣的,即求出【現在這次總區間被覆蓋的長度和上一次總區間被覆蓋的長度的差的絕對值】,另外怎么算豎線部分呢?首先我們要知道添加了這條橫線后會有多少條豎線,答案是2*num,所以為什么要記錄num呢,因為總區間被num條線段覆蓋,那么必定有2*num的端點,這些端點其實就是連著豎線,那么這些豎線的長度是多少呢?
就是【下一條橫線的高度-現在這條橫線的高度】,只要把這個高度乘上2*num即可
?
我采用的是第二種方法,這里不需要離散化,代碼如下:
#include <iostream> #include <string> #include <cstring> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm> #include <stack> #include <queue> #include <cctype> #include <vector> #include <iterator> #include <set> #include <map> #include <sstream> using namespace std;#define mem(a,b) memset(a,b,sizeof(a)) #define pf printf #define sf scanf #define spf sprintf #define pb push_back #define debug printf("!\n") #define MAXN 20000+5 #define MAX(a,b) a>b?a:b #define blank pf("\n") #define LL long long #define ALL(x) x.begin(),x.end() #define INS(x) inserter(x,x.begin()) #define pqueue priority_queue #define INF 0x3f3f3f3f#define ls (rt<<1) #define rs (rt<<1|1)int n,m;int col[MAXN<<2],len[MAXN<<2],lseg[MAXN<<2],rseg[MAXN<<2],segnum[MAXN<<2];struct node {int l,r,x,c;node(){}node(int a,int b,int c,int d):l(a),r(b),x(c),c(d){}bool operator < (const node &b) const{return x<b.x || x==b.x && c>b.c;} }a[MAXN];void PushUp(int rt,int l,int r) {if(col[rt]){len[rt] = r-l+1;segnum[rt]=2;lseg[rt] = rseg[rt] = 1;}else if(l==r) len[rt] = segnum[rt] = lseg[rt] = rseg[rt] = 0;else{len[rt] = len[ls]+len[rs];segnum[rt] = segnum[ls]+segnum[rs];lseg[rt] = lseg[ls];rseg[rt] = rseg[rs];if(lseg[rs] && rseg[ls]) segnum[rt]-=2;} }void update(int val,int L,int R,int l,int r,int rt) {if(L<=l && r<=R){col[rt] += val;PushUp(rt,l,r);return;}int mid = (l+r)>>1;if(L <= mid) update(val,L,R,l,mid,ls);if(R > mid) update(val,L,R,mid+1,r,rs);PushUp(rt,l,r); }int main() {int i,j,k,t,kase=1;while(~sf("%d",&n) && n){int left = 10000,right = -10000,v=0,ans=0,last=0;mem(len,0);mem(col,0);mem(segnum,0);mem(lseg,0);mem(rseg,0);for(i=0;i<n;i++){int x1,y1,x2,y2;sf("%d%d%d%d",&x1,&y1,&x2,&y2);a[v++] = node(y1,y2,x1,1);a[v++] = node(y1,y2,x2,-1);left = min(left,y1);right = max(right,y2);}//pf("%d %d\n",left,right);sort(a,a+v);for(i=0;i<v;i++){if(a[i].l < a[i].r) update(a[i].c,a[i].l,a[i].r-1,left,right,1);ans+= segnum[1]*(a[i+1].x-a[i].x);ans+= abs(len[1]-last);last = len[1];}pf("%d\n",ans);}return 0; }?
轉載于:https://www.cnblogs.com/qlky/p/5747719.html
總結
以上是生活随笔為你收集整理的ACM-线段树扫描线总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 分布式事务XA
- 下一篇: 使用Core Animation对象来实