[您有新的未分配科技点] 无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )

1500: [NOI2005]维修数列

Time Limit: 10 Sec  Memory Limit: 64 MB

Description

Input

输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目。
第2行包含N个数字,描述初始时的数列。
以下M行,每行一条命令,格式参见问题描述中的表格。
任何时刻数列中最多含有500 000个数,数列中任何一个数字均在[-1 000, 1 000]内。
插入的数字总数不超过4 000 000个,输入文件大小不超过20MBytes。

Output

对于输入数据中的GET-SUM和MAX-SUM操作,向输出文件依次打印结果,每个答案(数字)占一行。

Sample Input

9 8
2 -6 3 5 1 -5 -3 6 3
GET-SUM 5 4
MAX-SUM
INSERT 8 3 -5 7 2
DELETE 12 1
MAKE-SAME 3 3 2
REVERSE 3 6
GET-SUM 5 4
MAX-SUM

Sample Output

-1
10
1
10

HINT

 

 

 

这次我们学习无旋treap的区间操作(如果没有了解过无旋treap,你可以选择查看我上一篇讲解博文[您有新的未分配科技点]无旋treap:从好奇到入门

这道例题,真的是平衡树神题……

区间操作我们并没有见过,但我们可以联想一下单点操作时普通平衡树里都干了些什么:

"合并两棵树”和“把一棵树的前k个节点分裂出来”

可不可以用这些操作进行区间操作?显然是可以的,只要我们每次新建一棵树就可以了

在具体操作之前,我们先来看一下区间操作的原理:为什么平衡树的区间操作是对的?

我们知道,平衡树是一种二叉排序树,它的各种操作的前提是不会改变排序树的中序遍历

如果我们按照想要的中序遍历建树并且合并,我们一定能得到正确的区间,所以平衡树的区间操作是正确的。

这也是平衡树的两大建立方式之一:按中序遍历建树(另外一种是按权值建树……就是最常见的那种)

这样就涉及到一个没有出现的函数:build(建树)函数

build函数的原理和"笛卡尔树"的建造是比较像的(没有听说过笛卡尔树?请看笛卡尔树

我们用下面一张图来简要讲解一下build的构造过程(注意,定义key为维护堆性质的随机键值)

假设这是我的无旋treap目前的状态,我用一个栈来维护这棵树最右边的一条链,并且每一次在右下角处插入节点

假设我此时我们在9的右儿子添加了一个13,若13的key值小于栈顶元素9的key,那么就开始退栈,停止退栈的条件有两个,满足任意一个即停止:

1.当前栈顶元素key<13的key(约定key小的在上)
2.栈为空
若13的key>3的key并且<4的key,那么上图会变为:
这样显然还满足中序遍历,代码实现基本就是大模拟这种思路,不断退栈即可
我们看一下代码:
 1 Treap *stack[N],*x,*last;
 2 inline Treap *build()
 3 {
 4     int p=0;
 5     for(int i=1;i<=n;i++)
 6     {
 7         scanf("%d",&a[i]);
 8         x=newTreap(a[i]);last=null;
 9         while(p&&stack[p]->key > x->key)
10             {stack[p]->update();last=stack[p];stack[p--]=null;}
11         if(p)stack[p]->ch[1]=x;
12         x->ch[0]=last;stack[++p]=x;
13     }
14     while(p)stack[p--]->update();
15     return stack[1];
16 }

有了这个build操作,在加上之前的操作,我们很容易得出

insert=split+build+merge+merge

delete=split+split+merge

下面在考虑其他4个操作:

make-same:和线段树一样打标记并且下传就好

注意,这个操作会影响到最大连续和的求解,需要更新维护信息(更新谁请读者思考)。

reserve:这也是平衡树区间操作的经典考题……区间翻转问题

对于节点o,我们也是用一个标记来维护这个问题,

操作到这个节点时下传标记,然后用swap交换o的左右儿子即可。

不幸的是,这个操作也会影响到最大连续和的求解,也需要更新维护信息(更新谁请读者思考)。

get-sum:每个节点多维护一个sum附加值,在维护节点信息的时候更新即可。

get-max:这个操作也是一种经典的询问。

它需要我们多维护下面三个信息:最大前缀和,最大后缀和以及最大连续和。

对于每一个节点的最大前缀和,这个值可能是

1°它的左儿子的前缀和;

2°左儿子的和+它的val

3°左儿子的和+它的val+右儿子的前缀和

最大后缀和同理。

而最大连续和,这个范围可能完全在左儿子中,可能完全在右儿子中,也可能卡在左右儿子中间

所以我们可能的取值为:

1°左儿子的最大连续和;

2°右儿子的最大连续和

3°max(0,左儿子的后缀和)+它的val+max(右儿子的前缀和)

这样我们就解决了这个问题。觉得还是不太明白的同学可以去做一下cogs775山海经这道题,它就是单纯询问最大连续和一个操作。

这样,6个操作都被我们处理完了。本题不卡内存和时间,所以放心大胆的码就可以了……

代码见下:

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <ctime>
  5 using namespace std;
  6 const int N=500100;
  7 const int MAXN=4000100;
  8 const int inf=0x3f3f3f3f;
  9 int n,a[N],point;
 10 struct Treap
 11 {
 12     Treap *ch[2];
 13     int val,key,size,sum,l,r,m;bool flip,mark;
 14     Treap(){val=l=r=m=-inf;sum=0;size=0;mark=flip=0;key=rand();}
 15     inline void update()
 16     {
 17         size=ch[0]->size+ch[1]->size+1;
 18         sum=val+ch[0]->sum+ch[1]->sum;
 19         l=max(ch[0]->l,max(ch[0]->sum+val,ch[0]->sum+val+ch[1]->l));
 20         r=max(ch[1]->r,max(val+ch[1]->sum,ch[0]->r+val+ch[1]->sum));
 21         m=max(ch[0]->m,max(max(0,ch[0]->r)+val+max(0,ch[1]->l),ch[1]->m));
 22     }
 23 }*null=new Treap(),*root=null,*stack[N],*x,*last;
 24 typedef pair<Treap*,Treap*> D;
 25 inline void Maintain_flip(Treap *o)
 26 {
 27     if(o==null)return;
 28     o->flip^=1;swap(o->l,o->r);
 29 }
 30 inline void Maintain_mark(Treap *o,int c)
 31 {
 32     if(o==null)return;
 33     o->val=c;o->sum=o->size*c;
 34     o->l=o->r=o->m=max(o->size*c,c);
 35     o->mark=1;
 36 }
 37 inline void pushdown(Treap *o)
 38 {
 39     if(o==null)return;
 40     if(o->flip)
 41     {
 42         o->flip^=1;
 43         Maintain_flip(o->ch[0]);
 44         Maintain_flip(o->ch[1]);
 45         swap(o->ch[0],o->ch[1]);
 46     }
 47     if(o->mark)
 48     {
 49         Maintain_mark(o->ch[0],o->val);
 50         Maintain_mark(o->ch[1],o->val);
 51         o->mark=0;
 52     }
 53 }
 54 inline Treap* newTreap(int val)
 55 {
 56     Treap *o=new Treap();
 57     o->ch[1]=o->ch[0]=null;o->key=rand();
 58     o->val=o->sum=val;o->size=1;o->flip=o->mark=0;
 59     o->m=o->l=o->r=val;
 60     return o;
 61 }
 62 Treap *merge(Treap *a,Treap *b)
 63 {
 64     if(a==null)return b;
 65     if(b==null)return a;
 66     pushdown(a);pushdown(b);
 67     if(a->key < b->key)
 68         {a->ch[1]=merge(a->ch[1],b);a->update();return a;}
 69     else
 70         {b->ch[0]=merge(a,b->ch[0]);b->update();return b;}
 71 }
 72 D split(Treap *o,int k)
 73 {
 74     if(o==null) return D(null,null);
 75     D y;pushdown(o);
 76     if(o->ch[0]->size>=k)
 77         {y=split(o->ch[0],k);o->ch[0]=y.second;o->update();y.second=o;}
 78     else
 79         {y=split(o->ch[1],k-o->ch[0]->size-1);o->ch[1]=y.first;o->update();y.first=o;}
 80     return y;
 81 }
 82 inline Treap *build()
 83 {
 84     int p=0;
 85     for(int i=1;i<=n;i++)
 86     {
 87         scanf("%d",&a[i]);
 88         x=newTreap(a[i]);last=null;
 89         while(p&&stack[p]->key > x->key)
 90             {stack[p]->update();last=stack[p];stack[p--]=null;}
 91         if(p)stack[p]->ch[1]=x;
 92         x->ch[0]=last;stack[++p]=x;
 93     }
 94     while(p)stack[p--]->update();
 95     return stack[1];
 96 }
 97 void adjust(Treap *o)
 98 {
 99     if(o==null)return;
100     if(o->ch[0]!=null)adjust(o->ch[0]);
101     if(o->ch[1]!=null)adjust(o->ch[1]);
102     delete o;
103 }
104 inline void insert()
105 {
106     int pos;scanf("%d%d",&pos,&n);
107     Treap *o=build();
108     D x=split(root,pos);
109     root=merge(merge(x.first,o),x.second);
110 }
111 inline void erase()
112 {
113     int pos;scanf("%d%d",&pos,&n);
114     D x=split(root,pos-1);
115     D y=split(x.second,n);
116     adjust(y.first);
117     root=merge(x.first,y.second);
118 }
119 inline void reverse()
120 {
121     int pos;scanf("%d%d",&pos,&n);
122     D x=split(root,pos-1);
123     D y=split(x.second,n);
124     Maintain_flip(y.first);
125     root=merge(merge(x.first,y.first),y.second);
126 }
127 inline void make_same()
128 {
129     int pos,c;scanf("%d%d%d",&pos,&n,&c);
130     D x=split(root,pos-1);
131     D y=split(x.second,n);
132     Maintain_mark(y.first,c);
133     root=merge(merge(x.first,y.first),y.second);
134 }
135 inline int get_sum()
136 {
137     int pos;scanf("%d%d",&pos,&n);
138     if(n==0)return 0;
139     D x=split(root,pos-1);
140     D y=split(x.second,n);
141     int ret=y.first->sum;
142     root=merge(merge(x.first,y.first),y.second);
143     return ret;
144 }
145 int main()
146 {
147     int m;scanf("%d%d",&n,&m);root=build();
148     char opt[15];
149     while(m--)
150     {
151         scanf("%s",opt);
152         switch(opt[0])
153         {
154             case 'I':{insert();break;}
155             case 'D':{erase();break;}
156             case 'M':
157             {
158                 if(opt[2]=='K'){make_same();break;}
159                 else {printf("%d\n",root->m);break;}
160             }
161             case 'G':{printf("%d\n",get_sum());break;}
162             case 'R':{reverse();break;}
163         }
164     }
165 }
BZOJ1500
Progress is not created by contented people.
posted @ 2017-07-15 14:29  LadyLex  阅读(1136)  评论(2编辑  收藏  举报