kb-09-线段树--区间合并比较繁

  1 /*
  2    hdu-1540 题意:一个线段,长度为n,三种操作,Dx,挖掉某个点;R,恢复最近被挖掉的点;Qx查询该点所在的连续区间的长度;
  3    树的节点维护三个变量,该节点左边界开始连续的个数ll,右边界开始向左连续的个数rl,(在该区间内),该区间内最大的连续区间的长度ml;
  4    最后一个变量是为了方便判断,主要的还是前两个;
  5    修改的时候先是深入单点修改单点信息,然后回溯上来更父结点,更新的时候有点复杂;
  6    查询的时候借助ml的长度减少计算量,快速返回;
  7    区间合并;
  8 */
  9 #include<cstdio>
 10 #include<iostream>
 11 #include<cstring>
 12 #include<stack>
 13 #include<algorithm>
 14 using namespace std;
 15 struct Node
 16 {
 17     int l,r,ll,rl,ml;
 18 }tr[200005];
 19 void build(int rt,int l,int r)
 20 {
 21     tr[rt].l=l;
 22     tr[rt].r=r;
 23     tr[rt].ll=tr[rt].rl=tr[rt].ml=r-l+1;
 24     if(l==r)
 25         return ;
 26     int mid=(l+r)/2;
 27     build(rt<<1,l,mid);
 28     build(rt<<1|1,mid+1,r);
 29 }
 30 void Update(int rt,int l,int r,int t,int val)//t是要改的点;
 31 {
 32     if(l==r)                                //找到单点;
 33     {
 34         if(val==1)
 35         {
 36             tr[rt].ll=tr[rt].rl=tr[rt].ml=1;//改单点的信息;
 37         }
 38         else
 39         {
 40             tr[rt].ll=tr[rt].rl=tr[rt].ml=0;
 41         }
 42         return ;
 43     }
 44     int mid=(l+r)/2;//二分查找区间;
 45     if(t<=mid)
 46     {
 47         Update(rt<<1,l,mid,t,val);
 48     }
 49     else
 50     {
 51         Update(rt<<1|1,mid+1,r,t,val);
 52     }
 53 //    pushup操作;
 54     tr[rt].ll=tr[rt<<1].ll;                                        //左子树的左侧边界的值给父节点;
 55     tr[rt].rl=tr[rt<<1|1].rl;                                    //右子树的有边界的值给父结点;
 56     tr[rt].ml=max(tr[rt<<1].ml,tr[rt<<1|1].ml);                    //左右子树的最大连续数;
 57     tr[rt].ml=max(tr[rt].ml,tr[rt<<1].rl+tr[rt<<1|1].ll);        //两个子树的交接处的最大连续数;
 58     if(tr[rt<<1].ll==mid-l+1)
 59         tr[rt].ll+=tr[rt<<1|1].ll;                                //如果左子树是全连续的,就更新父节点的左边界最大连续数;
 60     if(tr[rt<<1|1].rl==r-mid)                                    //如果右子树是连续的,就更新父结点的有边界的最大连续数;
 61         tr[rt].rl+=tr[rt<<1].rl;
 62 }
 63 int Query(int rt,int l,int r,int t)//查询与这个点连接的最长序列;
 64 {
 65     if(l==r||tr[rt].ml==0||tr[rt].ml==r-l+1)  //如果找到单点,或该区间全空,或该区间全满;就返回;
 66         return tr[rt].ml;
 67     int mid=(l+r)/2;
 68     if(t<=mid)                                    //如果该点在左子树上
 69     {
 70         if(t>=tr[rt<<1].r-tr[rt<<1].rl+1)      //如果该点是在左子树右边界点连续范围,就进入左子树继续搜索该点并加上搜索右子树的左边界那个点的结果;
 71             return Query(rt<<1,l,mid,t)+Query((rt<<1)|1,mid+1,r,mid+1);
 72         else 
 73             return Query(rt<<1,l,mid,t);                     //如果不再右连续块内就进左子树继续搜索;
 74     }
 75     else//右子树;
 76     {
 77         if(t<=tr[rt<<1|1].l+tr[rt<<1|1].ll-1)       //在右子树的左侧连续的部分内,就加上左子树上的数据,左子树右边界的(不能用有子树的数据因为那不是全部的数据);
 78             return Query(rt<<1|1,mid+1,r,t)+Query(rt<<1,l,mid,mid);
 79         else
 80             return Query(rt<<1|1,mid+1,r,t);
 81     }
 82 }
 83 int main()
 84 {
 85     int n,m;
 86     while(scanf("%d%d",&n,&m)!=EOF&&n&&m)
 87     {
 88         memset(tr,0,sizeof(tr));
 89         stack<int > z;
 90         build(1,1,n);
 91         for(int i=0;i<m;i++)
 92         {
 93             char a[3];
 94             scanf("%s",a);
 95             if(a[0]=='D')
 96             {
 97                 int x;
 98                 scanf("%d",&x);
 99                 z.push(x);
100                 Update(1,1,n,x,0);      //0代表删除操作;
101             }
102             else if(a[0]=='R')
103             {
104                 if(!z.empty())
105                 {
106                     int x=z.top();
107                     z.pop();
108                     Update(1,1,n,x,1);   //1代表回复操作;
109                 }
110             }
111             else if(a[0]=='Q')
112             {
113                 int x;
114                 scanf("%d",&x);
115                 int ans=Query(1,1,n,x);
116                 printf("%d\n",ans);
117             }
118         }
119     }
120     return 0;
121 }

 

posted on 2015-05-31 23:01  bai_yan  阅读(153)  评论(0编辑  收藏  举报

导航