您的位置:首页 > 其它

线段树的懒操作:POJ1823

2012-10-13 11:25 369 查看
本来以为把伸展树看懂了,结果分析题目的时候,且不说自己做了,别人的代码都读不懂!看到伸展树操作中也有懒操作的身影,就重新回到基础,学习了一下线段树的懒操作问题!半天时间就这样过去了!不过还好的是勉强AC了这道题!这道题是一道线段树很基础的题:更新区间,查询区间!题目大意是给定一个区间,0表示空闲,1表示区间单元被占据。问整个区间最大的连续子区间有多大?记得以前做过类似的题目。线段树结点需要以下的信息:
struct Tree{
int ll,rr,mid;
int max,cl,cr;//max表示结点管理的区间最大的连续子区间有多大,cl表示区间的最左边有多少连续的区间,cr表示区间的右边有多少连续的子区间
int occ;//occ==0表示空,occ=1表示全部入住,occ=-1表示有空有住
};
由于查询的是整个区间的最大值,最后查询的时候只需要输出根结点的max即tree[1].max就可以了。为了提高线段树的效率,以相同的方式更新某区间时,(比如对整个区间同时add一个数)并不去真正更新其子区间,而是以某种方式把这个操作记录下来,等下一次访问到该区间并需要访问其子区间时,在访问的同时把子区间的值进行更改!这种思想就称为懒操作。像POJ的那道题就是典型的懒操作!思路很简单就不细说了,看代码可能更容易理解:
/** IntervalTree.cpp**  Created on: 2012-10-12*      Author: Administrator*/#include<stdio.h>#define max(a,b) (a)>(b)?(a):(b)#define M 20000struct Tree{
int ll,rr,mid;
int max,cl,cr;//max表示结点管理的区间最大的连续子区间有多大,cl表示区间的最左边有多少连续的区间,cr表示区间的右边有多少连续的子区间
int occ;//occ==0表示空,occ=1表示全部入住,occ=-1表示有空有住
};Tree tree[4*M];int n,p;void build(int id,int ll,int rr){tree[id].ll=ll,tree[id].rr=rr,tree[id].mid=(ll+rr)>>1;//刚开始建树,都是空的,所以可以这样写tree[id].max=tree[id].cl=tree[id].cr=rr-ll+1;tree[id].occ=0;if(ll==rr)return;build(id*2,ll,tree[id].mid);build(id*2+1,tree[id].mid+1,rr);}void push_down(int id,bool sign){//sign=true表示入住,sign=false表示退房tree[id].occ=-1;//表示有空有住tree[2*id].occ=tree[2*id+1].occ=sign;//表示全空或者全住if(sign){tree[2*id].cl=tree[2*id+1].cl=tree[2*id].cr=tree[2*id+1].cr=0;tree[2*id].max=tree[2*id+1].max=0;}else{int len=tree[2*id].rr-tree[2*id].ll+1;tree[2*id].cl=tree[2*id].cr=len;tree[2*id].max=len;len=tree[2*id+1].cr=tree[2*id+1].rr-tree[2*id+1].ll+1;tree[2*id+1].cl=len;tree[2*id+1].max=len;}}//更新区间void update(int id ,int ll,int rr,bool sign){//sign=true表示入住,sign=false表示退房if(tree[id].ll==ll&&tree[id].rr==rr){//找到区间tree[id].occ=sign;int len=tree[id].rr-tree[id].ll+1;sign==1?len=0:len;tree[id].cl=tree[id].cr=len;tree[id].max=len;return;}if(tree[id].occ!=-1)push_down(id,tree[id].occ);//执行到这行代码意味着 tree[id]的子区间要更改了,所以需要执行一次push_downif(rr<=tree[id].mid){update(id*2,ll,rr,sign);}else if(ll>tree[id].mid){update(id*2+1,ll,rr,sign);}else{update(id*2,ll,tree[id].mid,sign),update(id*2+1,tree[id].mid+1,rr,sign);}//需要修改的就只有3个值:max,cl,cr 分别代表最长连续个数为多少,最左边有多少个空闲,最右边有多少个空闲if(tree[id].occ==-1){//表示有空有住if(tree[2*id].occ==0){tree[id].cl=tree[id*2].cl+tree[2*id+1].cl;}else{tree[id].cl=tree[2*id].cl;}if(tree[2*id+1].occ==0){tree[id].cr=tree[id*2].cr+tree[2*id+1].cr;}else{tree[id].cr=tree[2*id+1].cr;}//求tree[id].maxint len=tree[2*id].cr+tree[2*id+1].cl;tree[id].max=max(len,max(tree[2*id].max,tree[2*id+1].max));}else{//表示全空或者全住int len;sign==0?len=0:len=tree[id].rr-tree[id].ll+1;tree[id].max=tree[id].cl=tree[id].cr=len;}if(tree[id*2].occ==tree[id*2+1].occ)tree[id].occ=tree[id*2].occ;}int main(){int i;scanf("%d %d",&n,&p);int sign;int ll,rr;build(1,1,n);for(i=0;i<p;i++){scanf("%d",&sign);if(sign==3){//outputprintf("%d\n",tree[1].max);}else{scanf("%d %d",&ll,&rr);rr=ll+rr-1;sign!=1? sign=0:sign=1;update(1,ll,rr,sign);}}return 0;}
相关的线段树懒操作的题目有:hdu2871,poj3468,poj3667


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息