线段树扫描线练习
概述:
扫描线是解决坐标系内矩形范围内统计问题的好方法(个人理解不一定对)
就维护一个区间,就像一根扫描线,沿这个区间垂直的方向遍历一个矩形区域(线动成面^_^)
遇到要求的答案更新至线段树中,岂不是很优秀。
应用:
1.求矩形面积:
大意:给几个矩形(有重叠部分),求整体面积。
解题思路:
就是很朴素的扫描线想法,将整个图形切割成若干个能够用底乘高求出的形状。
将输入按照y坐标排序,用线段树维护底长,每进入一条撤销或覆盖操作更新总边长,用总边长乘以高度差更新答案即可^_^
注意:长度为实数,做一下离散化就好了。
1 #include<map> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define lll spc<<1 6 #define rrr spc<<1|1 7 struct pct{ 8 double l,r; 9 double h; 10 int ll,rr; 11 int flag; 12 void ins(double le,double re,double hx,int ll,int rr,int fl) 13 { 14 l=le; 15 r=re; 16 h=hx; 17 ll=ll; 18 rr=rr; 19 flag=fl; 20 } 21 }pc[1000000]; 22 std::map<double,int>M; 23 int n; 24 int num[1000000]; 25 double len[1000000]; 26 double rx[1000000]; 27 bool cmp(pct x,pct y) 28 { 29 return x.h<y.h; 30 } 31 bool dcp(double x,double y) 32 { 33 return x<y; 34 } 35 namespace Sgt{ 36 void Spushup(int spc,int l,int r) 37 { 38 if(num[spc]) 39 len[spc]=rx[r+1]-rx[l]; 40 else if(l==r) 41 len[spc]=0.00; 42 else 43 len[spc]=len[lll]+len[rrr]; 44 return ; 45 } 46 void Sbuild(int l,int r,int spc) 47 { 48 len[spc]=0.00; 49 num[spc]=0; 50 if(l==r) 51 return ; 52 int mid=(l+r)>>1; 53 Sbuild(l,mid,lll); 54 Sbuild(mid+1,r,rrr); 55 return ; 56 } 57 void Supdate(int ll,int rr,int l,int r,int spc,int cmd) 58 { 59 if(ll>r||l>rr) 60 return ; 61 if(ll<=l&&r<=rr) 62 { 63 num[spc]+=cmd; 64 Spushup(spc,l,r); 65 return ; 66 } 67 int mid=(l+r)>>1; 68 Supdate(ll,rr,l,mid,lll,cmd); 69 Supdate(ll,rr,mid+1,r,rrr,cmd); 70 Spushup(spc,l,r); 71 } 72 } 73 int main() 74 { 75 int t=0; 76 while(true) 77 { 78 scanf("%d",&n); 79 if(!n) 80 return 0; 81 M.clear(); 82 int cnt=0; 83 double ans=0.00; 84 int rnt=0; 85 int dnt=0; 86 for(int i=1;i<=n;i++) 87 { 88 double le,re,hx,hy; 89 scanf("%lf%lf%lf%lf",&le,&hx,&re,&hy); 90 pc[++cnt].ins(le,re,hx,0,0,1); 91 pc[++cnt].ins(le,re,hy,0,0,-1); 92 rx[++rnt]=le; 93 rx[++rnt]=re; 94 } 95 std::sort(rx+1,rx+rnt+1,dcp); 96 for(int i=1;i<=rnt;i++) 97 { 98 if(M.find(rx[i])==M.end()) 99 M[rx[i]]=++dnt; 100 rx[dnt]=rx[i]; 101 } 102 for(int i=1;i<=cnt;i++) 103 { 104 pc[i].ll=M[pc[i].l]; 105 pc[i].rr=M[pc[i].r]; 106 } 107 std::sort(pc+1,pc+cnt+1,cmp); 108 Sgt::Sbuild(1,dnt,1); 109 double btn=0.00; 110 double lst=0.00; 111 for(int i=1;i<=cnt;i++) 112 { 113 ans+=btn*(pc[i].h-lst); 114 Sgt::Supdate(pc[i].ll,pc[i].rr-1,1,dnt,1,pc[i].flag); 115 btn=len[1]; 116 lst=pc[i].h; 117 } 118 t++; 119 printf("Test case #%d\n",t); 120 printf("Total explored area: %.2lf\n\n",ans); 121 } 122 return 0; 123 }
2.求矩形周长:
大意:给几个矩形(有重叠部分),求整体周长。
解题思路:
首先最朴素的扫描线算法就是,先扫一遍横线,再扫一遍竖线,最后统计答案
也就是说,我们只要统计横线或竖线就好了。
具体怎么做呢,将输入存为两条平行于扫描线的区间,第一遍叫加入,第二边叫取消,区间查询总长度-上次长度更新答案即可。
扫两遍嘛,比较麻烦,其实扫一遍是加入区间多少个不连续就好了这个就是竖边的数量再乘以竖边长度即可^_^
注意一下边的重合先处理顶边就好了。
代码(怕RE开大了线段树的区间就有点小慢):
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define lll spc<<1 5 #define rrr spc<<1|1 6 typedef long long lnt; 7 struct trnt{ 8 int nar; 9 int cl,cr; 10 int cvr; 11 int len; 12 void memd() 13 { 14 len=nar=cl=cr=cvr=0; 15 return ; 16 } 17 }tr[1000000]; 18 struct pct{ 19 int l,r; 20 int h; 21 int com; 22 }pc[1000000]; 23 int n; 24 int cnt; 25 namespace Sgt{ 26 void Sbuild(int l,int r,int spc) 27 { 28 tr[spc].memd(); 29 if(l==r) 30 return ; 31 int mid=(l+r)>>1; 32 Sbuild(l,mid,lll); 33 Sbuild(mid+1,r,rrr); 34 return ; 35 } 36 void Sdestory() 37 { 38 Sbuild(1,40000,1); 39 } 40 void Spushup(int spc,int l,int r) 41 { 42 if(tr[spc].cvr) 43 { 44 tr[spc].nar=1; 45 tr[spc].len=r-l+1; 46 tr[spc].cl=tr[spc].cr=1; 47 return ; 48 } 49 if(l==r) 50 { 51 tr[spc].memd(); 52 return ; 53 } 54 tr[spc].len=tr[lll].len+tr[rrr].len; 55 tr[spc].nar=tr[lll].nar+tr[rrr].nar; 56 tr[spc].cl=tr[lll].cl; 57 tr[spc].cr=tr[rrr].cr; 58 if(tr[lll].cr&&tr[rrr].cl) 59 tr[spc].nar--; 60 return ; 61 } 62 void Supdate(int l,int r,int ll,int rr,int spc,int cmd) 63 { 64 if(ll>r||l>rr) 65 return ; 66 if(ll<=l&&r<=rr) 67 { 68 tr[spc].cvr+=cmd; 69 Spushup(spc,l,r); 70 return ; 71 } 72 int mid=(l+r)>>1; 73 Supdate(l,mid,ll,rr,lll,cmd); 74 Supdate(mid+1,r,ll,rr,rrr,cmd); 75 Spushup(spc,l,r); 76 } 77 } 78 bool cmp(pct x,pct y) 79 { 80 if(x.h==y.h) 81 return x.com>y.com; 82 return x.h<y.h; 83 } 84 int main() 85 { 86 while(scanf("%d",&n)!=EOF) 87 { 88 lnt ans=0; 89 cnt=0; 90 Sgt::Sdestory(); 91 for(int i=1;i<=n;i++) 92 { 93 int ll,rr,hhx,hhy; 94 scanf("%d%d%d%d",&ll,&hhx,&rr,&hhy); 95 ll+=10005; 96 rr+=10005; 97 hhx+=10005; 98 hhy+=10005; 99 cnt++; 100 pc[cnt]=(pct){ll,rr,hhx,1}; 101 cnt++; 102 pc[cnt]=(pct){ll,rr,hhy,-1}; 103 } 104 std::sort(pc+1,pc+cnt+1,cmp); 105 int lst=tr[1].len; 106 for(int i=1;i<=cnt;i++) 107 { 108 Sgt::Supdate(1,30000,pc[i].l,pc[i].r-1,1,pc[i].com); 109 ans=ans+abs(tr[1].len-lst)+tr[1].nar*2*(pc[i+1].h-pc[i].h); 110 lst=tr[1].len; 111 } 112 printf("%lld\n",ans); 113 } 114 return 0; 115 }
3.平面内离散化点的统计:
这个就是将矩形区域内的答案存起来之后每进一个点查询整个矩形区域更新答案即可。