线段树的学习之:如何用线段树计算矩形面积(二)

    研究了线段树计算矩形面积的水题poj1151的实现过程,我又好好研究了用线段树来解决另一道稍微难一点的题:hdu3265,这是09年宁波赛区的一道题,只是在poj1151的基础上更巧妙了一点!

    由于题目给出的矩形是回字形,所以我把把一个回字拆开就可以了,也就是说,hdu3265中插入一个poster,相当于插入了四个矩形(也可能只有三个)!

    由于题目的数据量比较小,只有5000,而且是整型,所以不用离散化操作!在面前那道题的基础上,去掉离散化就OK了!

    把代码贴出来吧!

 
 
  1. #include<stdio.h> 
  2. #include<algorithm> 
  3. using namespace std; 
  4. #define M 50001 
  5. typedef long long llong; 
  6. struct Tree{ 
  7.     int ll,rr,cover;//这里的cover之所以用llong而不用bool是因为可能出现多条边重叠 
  8.     int len; 
  9. }tree[4*M]; 
  10. struct Line{ 
  11.     int x,y1,y2; 
  12.     int flag; 
  13. }line[8*M]; 
  14. //比较函数 
  15. bool cmp(const Line &l1,const Line &l2){//排序的比较函数 
  16.     if(l1.x==l2.x){ 
  17.         return l1.flag>l2.flag; 
  18.     } 
  19.     return l1.x<l2.x; 
  20. //建树 
  21. void build(int id,int ll,int rr){ 
  22.     tree[id].ll=ll;tree[id].rr=rr; 
  23.     tree[id].len=tree[id].cover=0; 
  24.     if(ll+1==rr)return;//如果已经是元线段,则不用拆分了 
  25.     int mid=(ll+rr)>>1; 
  26.     build(id*2,ll,mid); 
  27.     build(id*2+1,mid,rr); 
  28. void lenght(int id){//求用于计算的线段实际长度 
  29.     if(tree[id].cover>0){ 
  30.         tree[id].len=tree[id].rr-tree[id].ll; 
  31.     }else if(tree[id].ll+1==tree[id].rr){//元线段 
  32.         tree[id].len=0; 
  33.     }else 
  34.         tree[id].len=tree[id*2].len+tree[id*2+1].len; 
  35. //更新树 
  36. void update(int id,Line line){ 
  37.     if(tree[id].ll==line.y1&&tree[id].rr==line.y2){ 
  38.         tree[id].cover+=line.flag; 
  39.         lenght(id); 
  40.         return
  41.         //这里的比较有点特别,值得关注一下, 
  42.     }else if(line.y1>=tree[2*id+1].ll){//用右孩子的左边界来比较 
  43.         update(id*2+1,line); 
  44.     }else if(line.y2<=tree[id*2].rr){// 左孩子的右边界来比较 
  45.         update(id*2,line); 
  46.     }else{//分跨两个边界 
  47.         Line tmp; 
  48.         tmp=line;tmp.y2=tree[id*2].rr; 
  49.         update(2*id,tmp); 
  50.         tmp=line;tmp.y1=tree[id*2+1].ll; 
  51.         update(2*id+1,tmp); 
  52.     } 
  53.     lenght(id);//回溯的时候 修改,使根结点的len实时更新 
  54. void load(int id,int x,int y1,int y2,int flag){ 
  55.     line[id].x=x; 
  56.     line[id].y1=y1; 
  57.     line[id].y2=y2; 
  58.     line[id].flag=flag; 
  59.  
  60. int main(){ 
  61.     int n; 
  62.     int i,t; 
  63.     int Max=1; 
  64.     while(scanf("%d",&n)&&n){ 
  65.         int x1,y1,x2,y2,x3,y3,x4,y4; 
  66.         for(t=i=1;i<=n;i++){ 
  67.             scanf("%d %d %d %d %d %d %d %d",&x1,&y1,&x2,&y2,&x3,&y3,&x4,&y4); 
  68.                 ++x1,++y1,++x2,++y2,++x3,++y3,++x4,++y4;//由于起点是0,而线段树管理的起点是1,所以整体平移一个单位 
  69.             if(x1<x3&&y1<y2){ 
  70.                 load(t,x1,y1,y2,1);t++; 
  71.                 load(t,x3,y1,y2,-1);t++; 
  72.                 Max=max(Max,max(y1,y2)); 
  73.             } 
  74.             if(x3<x4&&y4<y2){ 
  75.                 load(t,x3,y4,y2,1);t++; 
  76.                 load(t,x4,y4,y2,-1);t++; 
  77.                 Max=max(Max,max(y4,y2)); 
  78.             } 
  79.             if(x3<x4&&y1<y3){ 
  80.                 load(t,x3,y1,y3,1);t++; 
  81.                 load(t,x4,y1,y3,-1);t++; 
  82.                 Max=max(Max,max(y1,y3)); 
  83.             } 
  84.             if(x4<x2&&y1<y2){ 
  85.                 load(t,x4,y1,y2,1);t++; 
  86.                 load(t,x2,y1,y2,-1);t++; 
  87.                 Max=max(Max,max(y1,y2)); 
  88.             } 
  89.         } 
  90.         sort(line+1,line+t,cmp); 
  91.         build(1,1,Max);//用y的最大值来建树 
  92.         update ( 1, line[1]);//第一条边一定是入边 
  93.         llong ans=0; 
  94.         for(i=2;i<t;i++){ 
  95.             ans+=(llong)tree[1].len*(llong)(line[i].x-line[i-1].x); 
  96.             update(1,line[i]);//最后一条边肯定是出边,不用考虑 
  97.         } 
  98.         printf("%I64d\n",ans); 
  99.  
  100.     } 
  101.     return 0; 

    经过这几天的思考,我对线段树的认识也更深入了一些!我们用线段树解决矩形相关的问题时,容易走进一个误区,那就是矩形是二维的,而通常我们学习的线段树是一维的!用一维的线段树来操作二维的区间,是很难让人想通其中的细节!

     但实际上,线段树操作的依然是一维的!这就需要我们把线段树操作线段(比如线段着色)的细节弄清楚!从我博客中关于线段树的两道题可以看出,线段树管理的只是X轴或者y轴的线段,

怎么管理呢?线段的插入或者删除!所以我理解到的用线段树线段树求矩形面积的本质就是:用线段树来插入或者删除线段,再本质一点:线段树的更新区间操作!

    再回到矩形的面积,用线段树其实用到了一种分割的思想,把原来的三个矩形分割成了5个矩形,X轴排序后,矩形的高是很容易求出来的,我们可以不管,但是矩形的宽度就不好求,这时候,线段树的作用就凸显出来了!所以线段树在这里,本质上还是管理线段,而不是矩形!

     
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值