线段树的学习之:如何用线段树计算矩形面积(二)
2012-11-03 10:09
232 查看
研究了线段树计算矩形面积的水题poj1151的实现过程,我又好好研究了用线段树来解决另一道稍微难一点的题:hdu3265,这是09年宁波赛区的一道题,只是在poj1151的基础上更巧妙了一点!
由于题目给出的矩形是回字形,所以我把把一个回字拆开就可以了,也就是说,hdu3265中插入一个poster,相当于插入了四个矩形(也可能只有三个)!
由于题目的数据量比较小,只有5000,而且是整型,所以不用离散化操作!在面前那道题的基础上,去掉离散化就OK了!
把代码贴出来吧!
经过这几天的思考,我对线段树的认识也更深入了一些!我们用线段树解决矩形相关的问题时,容易走进一个误区,那就是矩形是二维的,而通常我们学习的线段树是一维的!用一维的线段树来操作二维的区间,是很难让人想通其中的细节!
但实际上,线段树操作的依然是一维的!这就需要我们把线段树操作线段(比如线段着色)的细节弄清楚!从我博客中关于线段树的两道题可以看出,线段树管理的只是X轴或者y轴的线段,
怎么管理呢?线段的插入或者删除!所以我理解到的用线段树线段树求矩形面积的本质就是:用线段树来插入或者删除线段,再本质一点:线段树的更新区间操作!
再回到矩形的面积,用线段树其实用到了一种分割的思想,把原来的三个矩形分割成了5个矩形,X轴排序后,矩形的高是很容易求出来的,我们可以不管,但是矩形的宽度就不好求,这时候,线段树的作用就凸显出来了!所以线段树在这里,本质上还是管理线段,而不是矩形!
由于题目给出的矩形是回字形,所以我把把一个回字拆开就可以了,也就是说,hdu3265中插入一个poster,相当于插入了四个矩形(也可能只有三个)!
由于题目的数据量比较小,只有5000,而且是整型,所以不用离散化操作!在面前那道题的基础上,去掉离散化就OK了!
把代码贴出来吧!
#include<stdio.h> #include<algorithm> using namespace std; #define M 50001 typedef long long llong; struct Tree{ int ll,rr,cover;//这里的cover之所以用llong而不用bool是因为可能出现多条边重叠 int len; }tree[4*M]; struct Line{ int x,y1,y2; int flag; }line[8*M]; //比较函数 bool cmp(const Line &l1,const Line &l2){//排序的比较函数 if(l1.x==l2.x){ return l1.flag>l2.flag; } return l1.x<l2.x; } //建树 void build(int id,int ll,int rr){ tree[id].ll=ll;tree[id].rr=rr; tree[id].len=tree[id].cover=0; if(ll+1==rr)return;//如果已经是元线段,则不用拆分了 int mid=(ll+rr)>>1; build(id*2,ll,mid); build(id*2+1,mid,rr); } void lenght(int id){//求用于计算的线段实际长度 if(tree[id].cover>0){ tree[id].len=tree[id].rr-tree[id].ll; }else if(tree[id].ll+1==tree[id].rr){//元线段 tree[id].len=0; }else tree[id].len=tree[id*2].len+tree[id*2+1].len; } //更新树 void update(int id,Line line){ if(tree[id].ll==line.y1&&tree[id].rr==line.y2){ tree[id].cover+=line.flag; lenght(id); return; //这里的比较有点特别,值得关注一下, }else if(line.y1>=tree[2*id+1].ll){//用右孩子的左边界来比较 update(id*2+1,line); }else if(line.y2<=tree[id*2].rr){// 左孩子的右边界来比较 update(id*2,line); }else{//分跨两个边界 Line tmp; tmp=line;tmp.y2=tree[id*2].rr; update(2*id,tmp); tmp=line;tmp.y1=tree[id*2+1].ll; update(2*id+1,tmp); } lenght(id);//回溯的时候 修改,使根结点的len实时更新 } void load(int id,int x,int y1,int y2,int flag){ line[id].x=x; line[id].y1=y1; line[id].y2=y2; line[id].flag=flag; } int main(){ int n; int i,t; int Max=1; while(scanf("%d",&n)&&n){ int x1,y1,x2,y2,x3,y3,x4,y4; for(t=i=1;i<=n;i++){ scanf("%d %d %d %d %d %d %d %d",&x1,&y1,&x2,&y2,&x3,&y3,&x4,&y4); ++x1,++y1,++x2,++y2,++x3,++y3,++x4,++y4;//由于起点是0,而线段树管理的起点是1,所以整体平移一个单位 if(x1<x3&&y1<y2){ load(t,x1,y1,y2,1);t++; load(t,x3,y1,y2,-1);t++; Max=max(Max,max(y1,y2)); } if(x3<x4&&y4<y2){ load(t,x3,y4,y2,1);t++; load(t,x4,y4,y2,-1);t++; Max=max(Max,max(y4,y2)); } if(x3<x4&&y1<y3){ load(t,x3,y1,y3,1);t++; load(t,x4,y1,y3,-1);t++; Max=max(Max,max(y1,y3)); } if(x4<x2&&y1<y2){ load(t,x4,y1,y2,1);t++; load(t,x2,y1,y2,-1);t++; Max=max(Max,max(y1,y2)); } } sort(line+1,line+t,cmp); build(1,1,Max);//用y的最大值来建树 update ( 1, line[1]);//第一条边一定是入边 llong ans=0; for(i=2;i<t;i++){ ans+=(llong)tree[1].len*(llong)(line[i].x-line[i-1].x); update(1,line[i]);//最后一条边肯定是出边,不用考虑 } printf("%I64d\n",ans); } return 0; }
经过这几天的思考,我对线段树的认识也更深入了一些!我们用线段树解决矩形相关的问题时,容易走进一个误区,那就是矩形是二维的,而通常我们学习的线段树是一维的!用一维的线段树来操作二维的区间,是很难让人想通其中的细节!
但实际上,线段树操作的依然是一维的!这就需要我们把线段树操作线段(比如线段着色)的细节弄清楚!从我博客中关于线段树的两道题可以看出,线段树管理的只是X轴或者y轴的线段,
怎么管理呢?线段的插入或者删除!所以我理解到的用线段树线段树求矩形面积的本质就是:用线段树来插入或者删除线段,再本质一点:线段树的更新区间操作!
再回到矩形的面积,用线段树其实用到了一种分割的思想,把原来的三个矩形分割成了5个矩形,X轴排序后,矩形的高是很容易求出来的,我们可以不管,但是矩形的宽度就不好求,这时候,线段树的作用就凸显出来了!所以线段树在这里,本质上还是管理线段,而不是矩形!
相关文章推荐
- 线段树的学习之:如何用线段树计算矩形面积
- 线段树辅助——扫描线法计算矩形面积并
- 线段树辅助——扫描线法计算矩形面积并
- 线段树辅助——扫描线法计算矩形面积并
- 要素外接矩形的四个角点坐标、长度、宽度、面积如何计算到要素属性表中?
- 线段树 - 扫描线法计算矩形面积
- 如何在Java中创建一个矩形类,并通过set()传入长与宽,计算矩形的周长与面积并通过get()查看计算结果
- 【java学习记录】2.定义一个计算矩形面积、立方体和球体体积的类,该类完成计算的方法用静态方法实现
- 【java学习记录】7.定义一个接口ArearInterface,其中包含一个方法,计算面积三角形、矩形、圆形的面积
- HDU - 1255 覆盖的面积(线段树-矩形交面积)
- 深度学习中如何计算图片数据的均值
- HDU 1264 Counting Squares (线段树-扫描线-矩形面积并)
- hdu1542 线段树扫描线求矩形面积的并
- 线段树学习之:用线段数统计矩形的周长hdu(1828)
- HDU3265(线段树+扫描线+挖空矩形面积并)
- 如何求两个矩形相交面积
- 算法总结:【线段树+扫描线】&矩形覆盖求面积/周长问题(HDU 1542/HDU 1828)
- 【学习笔记--视频拼接】如何提高计算效率 part_1
- hdu 3265 Posters(矩形面积并 扫描线+线段树)
- poj 3277 City Horizon (线段树 扫描线 矩形面积并)