线段树的懒操作:POJ1823

    本来以为把伸展树看懂了,结果分析题目的时候,且不说自己做了,别人的代码都读不懂!看到伸展树操作中也有懒操作的身影,就重新回到基础,学习了一下线段树的懒操作问题!

    半天时间就这样过去了!不过还好的是勉强AC了这道题!

    这道题是一道线段树很基础的题:更新区间,查询区间!题目大意是给定一个区间,0表示空闲,1表示区间单元被占据。问整个区间最大的连续子区间有多大?

    记得以前做过类似的题目。线段树结点需要以下的信息:

 

 
 
  1. struct Tree{ 
  2.     int ll,rr,mid; 
  3.     int max,cl,cr;//max表示结点管理的区间最大的连续子区间有多大,cl表示区间的最左边有多少连续的区间,cr表示区间的右边有多少连续的子区间 
  4.     int occ;//occ==0表示空,occ=1表示全部入住,occ=-1表示有空有住 
  5. }; 

由于查询的是整个区间的最大值,最后查询的时候只需要输出根结点的max即tree[1].max就可以了。

   为了提高线段树的效率,以相同的方式更新某区间时,(比如对整个区间同时add一个数)并不去真正更新其子区间,而是以某种方式把这个操作记录下来,等下一次访问到该区间并需要访问其子区间时,在访问的同时把子区间的值进行更改!这种思想就称为懒操作。

    像POJ的那道题就是典型的懒操作!

    思路很简单就不细说了,看代码可能更容易理解:

 
 
  1. /* 
  2.  * IntervalTree.cpp 
  3.  * 
  4.  *  Created on: 2012-10-12 
  5.  *      Author: Administrator 
  6.  */ 
  7. #include<stdio.h> 
  8. #define max(a,b) (a)>(b)?(a):(b) 
  9. #define M 20000 
  10. struct Tree{ 
  11.     int ll,rr,mid; 
  12.     int max,cl,cr;//max表示结点管理的区间最大的连续子区间有多大,cl表示区间的最左边有多少连续的区间,cr表示区间的右边有多少连续的子区间 
  13.     int occ;//occ==0表示空,occ=1表示全部入住,occ=-1表示有空有住 
  14. }; 
  15. Tree tree[4*M]; 
  16. int n,p; 
  17. void build(int id,int ll,int rr){ 
  18.     tree[id].ll=ll,tree[id].rr=rr,tree[id].mid=(ll+rr)>>1; 
  19.     //刚开始建树,都是空的,所以可以这样写 
  20.     tree[id].max=tree[id].cl=tree[id].cr=rr-ll+1; 
  21.     tree[id].occ=0; 
  22.     if(ll==rr)return
  23.     build(id*2,ll,tree[id].mid); 
  24.     build(id*2+1,tree[id].mid+1,rr); 
  25. void push_down(int id,bool sign){//sign=true表示入住,sign=false表示退房 
  26.     tree[id].occ=-1;//表示有空有住 
  27.     tree[2*id].occ=tree[2*id+1].occ=sign;//表示全空或者全住 
  28.     if(sign){ 
  29.         tree[2*id].cl=tree[2*id+1].cl=tree[2*id].cr=tree[2*id+1].cr=0; 
  30.         tree[2*id].max=tree[2*id+1].max=0; 
  31.     }else
  32.         int len=tree[2*id].rr-tree[2*id].ll+1; 
  33.         tree[2*id].cl=tree[2*id].cr=len; 
  34.         tree[2*id].max=len; 
  35.         len=tree[2*id+1].cr=tree[2*id+1].rr-tree[2*id+1].ll+1; 
  36.         tree[2*id+1].cl=len; 
  37.         tree[2*id+1].max=len; 
  38.     } 
  39. //更新区间 
  40. void update(int id ,int ll,int rr,bool sign){//sign=true表示入住,sign=false表示退房 
  41.     if(tree[id].ll==ll&&tree[id].rr==rr){//找到区间 
  42.         tree[id].occ=sign; 
  43.         int len=tree[id].rr-tree[id].ll+1; 
  44.         sign==1?len=0:len; 
  45.         tree[id].cl=tree[id].cr=len; 
  46.         tree[id].max=len; 
  47.         return
  48.     } 
  49.     if(tree[id].occ!=-1)push_down(id,tree[id].occ);//执行到这行代码意味着 tree[id]的子区间要更改了,所以需要执行一次push_down 
  50.     if(rr<=tree[id].mid){ 
  51.         update(id*2,ll,rr,sign); 
  52.     }else if(ll>tree[id].mid){ 
  53.         update(id*2+1,ll,rr,sign); 
  54.     }else
  55.         update(id*2,ll,tree[id].mid,sign), 
  56.         update(id*2+1,tree[id].mid+1,rr,sign); 
  57.     } 
  58.     //需要修改的就只有3个值:max,cl,cr 分别代表最长连续个数为多少,最左边有多少个空闲,最右边有多少个空闲 
  59.     if(tree[id].occ==-1){//表示有空有住 
  60.         if(tree[2*id].occ==0){ 
  61.             tree[id].cl=tree[id*2].cl+tree[2*id+1].cl; 
  62.         }else
  63.             tree[id].cl=tree[2*id].cl; 
  64.         } 
  65.         if(tree[2*id+1].occ==0){ 
  66.             tree[id].cr=tree[id*2].cr+tree[2*id+1].cr; 
  67.         }else
  68.             tree[id].cr=tree[2*id+1].cr; 
  69.         } 
  70.         //求tree[id].max 
  71.         int len=tree[2*id].cr+tree[2*id+1].cl; 
  72.         tree[id].max=max(len,max(tree[2*id].max,tree[2*id+1].max)); 
  73.     }else{//表示全空或者全住 
  74.         int len; 
  75.         sign==0?len=0:len=tree[id].rr-tree[id].ll+1; 
  76.         tree[id].max=tree[id].cl=tree[id].cr=len; 
  77.     } 
  78.     if(tree[id*2].occ==tree[id*2+1].occ) 
  79.         tree[id].occ=tree[id*2].occ; 
  80. int main(){ 
  81.     int i; 
  82.     scanf("%d %d",&n,&p); 
  83.     int sign; 
  84.     int ll,rr; 
  85.     build(1,1,n); 
  86.     for(i=0;i<p;i++){ 
  87.         scanf("%d",&sign); 
  88.         if(sign==3){ 
  89.             //output 
  90.             printf("%d\n",tree[1].max); 
  91.         }else
  92.             scanf("%d %d",&ll,&rr); 
  93.             rr=ll+rr-1; 
  94.             sign!=1? sign=0:sign=1; 
  95.             update(1,ll,rr,sign); 
  96.         } 
  97.     } 
  98.     return 0; 

 

 

 

 

  相关的线段树懒操作的题目有:hdu2871,poj3468,poj3667

 

 
 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值