今天我们也继续精神满满的可持久化——这次我带来的是可持久化平衡树的讲解。
可持久化平衡树,顾名思义,和主席树一样支持历史版本的查询。
可持久化平衡树都可以用什么实现呢?朴素的二叉排序树,或者无旋Treap,或者替罪羊。其他的平衡树都不能实现可持久化
……好吧,我们直接把二叉排序树扔掉。下面我们来想一下。
splay是均摊的log复杂度,这是不清真的
可能某一次的复杂度很高,就导致复制了很多节点,因此是可以卡的……
但是换成重量平衡树就没事了。
我们想一想,重量平衡树:Treap和替罪羊
替罪羊要拍扁重建,这样之前可持久化的关系会被改变
有旋的Treap,因为要旋转,更新父子关系也很复杂
因此只剩下无旋Treap了2333
我们只需要在进行merge和split操作的时候进行可持久化,复制一下经过的节点即可。
在具体的代码实现上说,我们只多了一个函数:copy函数,用来复制(好像特意开一个函数不太值……233)
1 inline void cop(Treap *&a,Treap *b) 2 { 3 if(b==null)a=null; 4 else a=newTreap(0),*a=*b; 5 //这个函数的关键点有二:一是a要传引用,二是必须写*a=*b。 6 //*a=*b只把b存储的值赋给了a,而对a的操作不会影响b的值,即实现了可持久化 7 }
UPD 感谢rvalue dalao上面的a是传引用……我知识水平不够把这俩弄混了……
这样,我们只需要在merge和split函数中调用上面的cop函数来复制节点,就可以实现可持久化了。
更改后的split和merge代码如下
1 void split(Treap *rt,Treap *&a,Treap *&b,int k) 2 { 3 if(!k)cop(b,rt),a=null; 4 else if(rt->size<=k)cop(a,rt),b=null; 5 else if(rt->ch[0]->size>=k) 6 cop(b,rt),split(rt->ch[0],a,b->ch[0],k),b->update(); 7 else cop(a,rt),split(rt->ch[1],a->ch[1],b,k-rt->ch[0]->size-1),a->update(); 8 } 9 void merge(Treap *&rt,Treap *a,Treap *b) 10 { 11 if(a==null)cop(rt,b); 12 else if(b==null)cop(rt,a); 13 else if(a->key < b->key) 14 cop(rt,a),merge(rt->ch[1],a->ch[1],b),rt->update(); 15 else 16 cop(rt,b),merge(rt->ch[0],a,b->ch[0]),rt->update(); 17 }
这样,我们就成功的实现了可持久化:)
给出一道例题 cogs2314 Persistable Editor http://cogs.pro/cogs/problem/problem.php?pid=2314
这道题就是在对序列维护实现一个可持久化的平衡树。直接利用无旋Treap实现即可。
下面给出我的代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <ctime> 5 #include <cstdlib> 6 using namespace std; 7 const int N=100005; 8 int d,cnt;char s[250]; 9 struct Treap 10 { 11 Treap *ch[2]; 12 char val;int size,key; 13 Treap(){val=size=0;key=rand();ch[0]=ch[1]=NULL;} 14 inline void update(){size=ch[0]->size+ch[1]->size+1;} 15 }*null=new Treap(),*root[N]; 16 inline Treap* newTreap(char c) 17 { 18 Treap *o=new Treap();o->ch[0]=o->ch[1]=null; 19 o->val=c;o->size=1;return o; 20 } 21 inline void cop(Treap *&a,Treap *b) 22 { 23 if(b==null)a=null; 24 else a=newTreap(0),*a=*b; 25 } 26 void split(Treap *rt,Treap *&a,Treap *&b,int k) 27 { 28 if(!k)cop(b,rt),a=null; 29 else if(rt->size<=k)cop(a,rt),b=null; 30 else if(rt->ch[0]->size>=k) 31 cop(b,rt),split(rt->ch[0],a,b->ch[0],k),b->update(); 32 else cop(a,rt),split(rt->ch[1],a->ch[1],b,k-rt->ch[0]->size-1),a->update(); 33 } 34 void merge(Treap *&rt,Treap *a,Treap *b) 35 { 36 if(a==null)cop(rt,b); 37 else if(b==null)cop(rt,a); 38 else if(a->key < b->key) 39 cop(rt,a),merge(rt->ch[1],a->ch[1],b),rt->update(); 40 else 41 cop(rt,b),merge(rt->ch[0],a,b->ch[0]),rt->update(); 42 } 43 void dfs(Treap *o) 44 { 45 if(o==null)return; 46 dfs(o->ch[0]); 47 printf("%c",o->val); 48 if(o->val=='c')d++; 49 dfs(o->ch[1]); 50 } 51 inline void print() 52 { 53 int mk,p,x; 54 scanf("%d%d%d",&mk,&p,&x);mk-=d,p-=d,x-=d; 55 Treap *a,*b,*c; 56 split(root[mk],a,b,p-1),split(b,b,c,x); 57 dfs(b);printf(" "); 58 merge(a,a,b),merge(root[mk],a,c); 59 } 60 inline Treap* build(char *t) 61 { 62 static Treap *stack[210],*x,*last; 63 int p=0,m=strlen(t); 64 for(int i=0;i<m;i++) 65 { 66 x=newTreap(s[i]);last=null; 67 while(p&&stack[p]->key > x->key) 68 {stack[p]->update();last=stack[p];stack[p--]=null;} 69 if(p)stack[p]->ch[1]=x; 70 x->ch[0]=last;stack[++p]=x; 71 } 72 while(p)stack[p--]->update(); 73 return stack[1]; 74 } 75 inline void insert() 76 { 77 int p;scanf("%d%s",&p,s);p-=d; 78 Treap *a,*b,*c=build(s); 79 split(root[cnt],a,b,p); 80 merge(a,a,c),merge(root[++cnt],a,b); 81 } 82 inline void del() 83 { 84 int p,x;scanf("%d%d",&p,&x);p-=d,x-=d; 85 Treap *a,*b,*c; 86 split(root[cnt],a,b,p-1),split(b,b,c,x); 87 merge(root[++cnt],a,c); 88 } 89 int main() 90 { 91 int n,opt;scanf("%d",&n); 92 null->ch[0]=null->ch[1]=null; 93 for(int i=0;i<=n;i++)root[i]=null; 94 while(n--) 95 { 96 scanf("%d",&opt); 97 switch(opt) 98 { 99 case 1:insert();break; 100 case 2:del();break; 101 case 3:print();break; 102 } 103 } 104 }
平衡树是一种很强大的数据结构,而把它可持久化以后必然让他如虎添翼,能带来更多新的题型以及新的做法。希望你能从我的博客中学到什么:)