无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )
来源:互联网 发布:mac上装锐捷客户端 编辑:程序博客网 时间:2024/05/29 09:41
转自ZZH大佬,原文:http://www.cnblogs.com/LadyLex/p/7182631.html
1500: [NOI2005]维修数列
Time Limit: 10 Sec Memory Limit: 64 MBDescription
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
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
10
1
10
HINT
这次我们学习无旋treap的区间操作(如果没有了解过无旋treap,你可以选择查看我上一篇讲解博文[您有新的未分配科技点]无旋treap:从好奇到入门)
这道例题,真的是平衡树神题……
区间操作我们并没有见过,但我们可以联想一下单点操作时普通平衡树里都干了些什么:
"合并两棵树”和“把一棵树的前k个节点分裂出来”
可不可以用这些操作进行区间操作?显然是可以的,只要我们每次新建一棵树就可以了
在具体操作之前,我们先来看一下区间操作的原理:为什么平衡树的区间操作是对的?
我们知道,平衡树是一种二叉排序树,它的各种操作的前提是不会改变排序树的中序遍历
如果我们按照想要的中序遍历建树并且合并,我们一定能得到正确的区间,所以平衡树的区间操作是正确的。
这也是平衡树的两大建立方式之一:按中序遍历建树(另外一种是按权值建树……就是最常见的那种)
这样就涉及到一个没有出现的函数:build(建树)函数
build函数的原理和"笛卡尔树"的建造是比较像的(没有听说过笛卡尔树?请看笛卡尔树)
我们用下面一张图来简要讲解一下build的构造过程(注意,定义key为维护堆性质的随机键值)
假设这是我的无旋treap目前的状态,我用一个栈来维护这棵树最右边的一条链,并且每一次在右下角处插入节点
假设我此时我们在9的右儿子添加了一个13,若13的key值小于栈顶元素9的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 }
- 无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )
- splay BZOJ1500 NOI2005 维护数列
- BZOJ1500 维护数列 treap实现
- [NOI2005] 维护数列(fhq-Treap)
- [BZOJ1500][NOI2005]维修数列(Splay)
- [BZOJ1500][NOI2005]维修数列(splay)
- BZOJ1500: [NOI2005]维修数列(平衡树)
- bzoj1500 [NOI2005]维修数列(splay)
- BZOJ1500: [NOI2005]维修数列(洛谷P2042)
- [bzoj1500][NOI2005]维修数列
- bzoj1500: [NOI2005]维修数列
- BZOJ1500 [NOI2005]维修数列
- bzoj1500: [NOI2005]维修数列
- [BZOJ1500][NOI2005]维修数列
- [BZOJ1500][NOI2005]维修数列
- bzoj1500【NOI2005】维修数列
- bzoj1500: [NOI2005]维修数列
- 【bzoj1500】[NOI2005]维修数列
- ARC与MRC的理解
- Dialog 去除边缘框 附带背景不变暗
- JavaSE之线程Thread
- 关注民生民情——华北水利水电大学“情艺”国情社情调查
- HDU 6069 Counting Divisors(素数筛法+枚举+技巧)——2017 Multi-University Training Contest
- 无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )
- [bzoj1385][Baltic2000]Division expression
- java设计模式之单例设计
- Swift和Objective-C混编要点
- IO的部分方法(1)
- POJ 1258 Agri-Net 【最小生成树入门题目】【prime模板】
- docker修改配置文件导致无法正常使用
- 【剑指offer】面试题57(2):和为S的连续正数序列
- c语言中a|b 是什么意思