学习如何用线段树来统计矩形的并面积后,接下来我学习一下如何用线段树来统计矩形周长的并!尽管其基本思路跟统计矩形的面积差不多,但估计是因为没有完全明白线段树的思想,我还是没有想出如何用线段树统计线段树的面积!
在学习的过程中参考了以下两个博客的代码,最终以博客二的代码作为研究对象!
博客一:http://www.notonlysuccess.com/index.php/divide-tree/
博客二:http://hi.baidu.com/lw0090s/item/159f6ad6cab61b9c260ae731
个人觉得博客二的代码更适合于像我这样的菜鸟!更容易理解!把代码贴出来!(注代码来源于博客二,不是本人写出来的)
- #include<iostream>
- #include<algorithm>
- #include<stdio.h>
- using namespace std;
- struct Line
- {
- int x,y1,y2;
- int flag;
- } line[5005*2];
- int y[5005*2];
- struct tree
- {
- int lines,len,l,r,c,lb,rb; //lb表示线段左边端点是否被覆盖为0,1,rb同理右边
- //lines表示当前线段下面覆盖的间隔的元线段的条数,len表示覆盖的长度
- } tree[5005*2*5];
- int cmp(Line a,Line b)
- {
- if(a.x!=b.x)
- return a.x<b.x;
- else
- return a.flag>b.flag;
- }
- /*
- 建树不用说,很直观明白!就是以元线段来建树的 而且用到的离散化
- */
- void build(int l,int r,int num)
- {
- tree[num].l=l;
- tree[num].r=r;
- tree[num].lines=0;
- tree[num].len=0;
- tree[num].c=0;
- tree[num].lb=0;
- tree[num].rb=0;
- if(l==r-1)
- return;
- int mid=(l+r)/2;
- build(l,mid,2*num);
- build(mid,r,2*num+1);
- }
- /*
- 个人感觉这里写得特别清晰,计算长度也很简单
- */
- void calen(int num)
- {
- if(tree[num].c>0)
- tree[num].len=y[tree[num].r]-y[tree[num].l];//这里典型的以下标来建树
- else
- {
- if(tree[num].l==tree[num].r-1)//元线段
- tree[num].len=0;
- else
- tree[num].len=tree[num*2].len+tree[num*2+1].len;
- }
- return;
- }
- /*
- 这里统计线段的条数不好理解,我看第一种代码的时候就没有看懂 lb与rb的意思!
- 在这里依然有点朦胧,感觉没有理解透!
- */
- void caline(int num)
- {
- tree[num].lines=0;
- tree[num].lb=0;
- tree[num].rb=0;
- if(tree[num].c>0)
- {
- tree[num].lines=2;
- tree[num].rb=1;
- tree[num].lb=1;
- }
- else
- {
- if(tree[num].l==tree[num].r-1)//元线段
- tree[num].lines=0;
- else
- {
- tree[num].lines=tree[num*2].lines+tree[2*num+1].lines-2*(tree[num*2].rb&tree[num*2+1].lb);
- //这里就是lb和rb标记的原因,在合并时使用
- tree[num].lb=tree[num*2].lb;
- tree[num].rb=tree[num*2+1].rb;
- }
- }
- return;
- }
- void update(Line e,int num)
- {
- if(e.y1==y[tree[num].l]&&e.y2==y[tree[num].r])//与交和并的区别是这里插入到底了,
- //因为这里要计算下面的元线段得条数,所以必须插入到底
- {
- tree[num].c+=e.flag;
- tree[num].lb=1;
- tree[num].rb=1;//标记当前线段的端点的覆盖情况
- }
- else if(e.y2<=y[tree[num*2].r])
- update(e,num*2);
- else if(e.y1>=y[tree[num*2+1].l])
- update(e,num*2+1);
- else
- {
- Line tmp=e;
- int mid=(tree[num].l+tree[num].r)/2;
- tmp.y2=y[mid];
- update(tmp,num*2);
- tmp=e;
- tmp.y1=y[mid];
- update(tmp,num*2+1);
- }
- calen(num);//计算长度
- caline(num); //计算元线段的条数
- }
- int main()
- {
- int i,j,n;
- int x1,y1,x2,y2,t;
- int lenth,lines,ans;
- while(scanf("%d",&n)!=EOF)
- {
- t=1;
- for(i=1; i<=n; i++)
- {
- scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
- line[t].x=x1;
- line[t].y1=y1;
- line[t].y2=y2;
- line[t].flag=1;//建立标记要看x的坐标大小
- line[t+1].x=x2;
- line[t+1].y1=y1;
- line[t+1].y2=y2;
- line[t+1].flag=-1;
- y[t]=y1;
- y[t+1]=y2;
- t=t+2;
- }
- sort(line+1,line+t,cmp);
- sort(y+1,y+t);
- int l=1;
- for(i=2; i<t; i++)//线段的离散化
- {
- if(y[i]!=y[i-1])
- y[++l]=y[i];//这里因为要看元线段的条数所以将相同的点去掉
- }
- build(1,l,1);
- lenth=0;
- lines=0;
- ans=0;
- for(i=1; i<t; i++)
- {
- update(line[i],1);
- ans+=abs(lenth-tree[1].len)+lines*(line[i].x-line[i-1].x);
- lenth=tree[1].len;
- lines=tree[1].lines;
- }
- printf("%d\n",ans);
- }
- // system("pause");
- }
先把代码收藏着吧!以后学习的时候慢慢来参悟!