可持久化Treap(fhq Treap,非旋转式Treap)学习(未完待续)
Posted 安
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可持久化Treap(fhq Treap,非旋转式Treap)学习(未完待续)相关的知识,希望对你有一定的参考价值。
int build(int *data,int n) { int x,last=0;static int sta[maxn],top; for(int i=1;i<=n;i++) { x=new_node(data[i]),last=0; while(top&&fix[sta[top]]>fix[x]) update(sta[top]),last=sta[top],sta[top--]=0; if(top) ch[sta[top]][1]=x; ch[x][0]=last;sta[++top]=x; } while(top) update(sta[top--]); return sta[1]; }
1: split
将Treap按照权值或排名分裂为两棵Treap 我只写了按权值分裂
对于我们遍历到每一个点,假如它的权值小于k,那么它的所有左子树,都要分到左边的树里,然后遍历它的右儿子。假如大于k,把它的所有右子树分到右边的树里,遍历左儿子。
因为它的最多操作次数就是一直分到底,效率就是O(logn)。
void split(int now,int k,int &x,int &y) { if(!now) x=y=0; else { if(val[now]<=k) x=now,split(ch[now][1],k,ch[now][1],y); else y=now,split(ch[now][0],k,x,ch[now][0]); update(now); } }
2: merge
这个就是把两个Treap合成一个,保证第一个的权值小于第二个。
因为第一个Treap的权值都比较小,我们比较一下它的fix(附加权值),假如第一个的fix小,我们就可以直接保留它的所有左子树,接着把第一个Treap变成它的右儿子。反之,我们可以保留第二棵的所有右子树,指针指向左儿子。
你可以把这个过程形象的理解为在第一个Treap的左子树上插入第二个树,也可以理解为在第二个树的左子树上插入第一棵树。因为第一棵树都满足小于第二个树,所以就变成了比较fix来确定树的形态。
也就是说,我们其实是遍历了第一个trep的根->最大节点,第二个Treap的根->最小节点,也就是O(logn)
int merge(int A,int B) { if(!A||!B) return A+B; if(fix[A]<fix[B]){ch[A][1]=merge(ch[A][1],B); update(A); return A;} else {ch[B][0]=merge(A,ch[B][0]); update(B); return B;} }
下面我们就可以通过这两个基本的东西实现各种各样的操作了。
一:基本操作
insertinsert
插入一个权值为v的点,把树按照v的权值split成两个,再按照顺序merge回去。
deldel
删除权值为v的点,把树按照v分成两个a,b,再把a按照v-1分成c,d。把c的两个子儿子merge起来,再merge(merge(c,d),b)
(因为把c的两个儿子merge起来之后,如果权值为v的节点有一个,就已经把他删除了,因为merge后c=0;若有多个就删除了一个)
precursorprecursor
找前驱的话把root按v-1 split成x,y,在x里面找最大值
successorsuccessor
找后继的话把root按v split成x,y,在y里找最小值
rankrank
把root按v-1 split成x,y,排名是x的siz
代码:https://www.luogu.org/problem/show?pid=3369 普通平衡树
#include<iostream> #include<cstdio> #include<cstring> #include<ctime> #include<cstdlib> #define maxn 500001 using namespace std; int size[maxn],ch[maxn][2],fix[maxn],val[maxn]; int T,cnt,n,m,x,y,z,p,a,root,com; inline int read() { int x=0,f=1;char c=getchar(); while(c>\'9\'||c<\'0\'){if(c==\'-\')f=-1;c=getchar();} while(c>=\'0\'&&c<=\'9\'){x=x*10+c-\'0\';c=getchar();} return x*f; } inline void update(int x) { size[x]=1+size[ch[x][0]]+size[ch[x][1]]; } inline int new_node(int x) { size[++cnt]=1; val[cnt]=x; fix[cnt]=rand(); return cnt; } int merge(int A,int B) { if(!A||!B) return A+B; if(fix[A]<fix[B]){ch[A][1]=merge(ch[A][1],B); update(A); return A;} else {ch[B][0]=merge(A,ch[B][0]); update(B); return B;} } void split(int now,int k,int &x,int &y) { if(!now) x=y=0; else { if(val[now]<=k) x=now,split(ch[now][1],k,ch[now][1],y); else y=now,split(ch[now][0],k,x,ch[now][0]); update(now); } } int kth(int now,int k) { while(1) { if(k<=size[ch[now][0]]) now=ch[now][0]; else if(k==size[ch[now][0]]+1) return now; else k-=size[ch[now][0]]+1,now=ch[now][1]; } } int main() { srand((unsigned)time(NULL)); T=read(); while(T--) { p=read();a=read(); if(p==1) { split(root,a,x,y); root=merge(merge(x,new_node(a)),y); } else if(p==2) { split(root,a,x,z); split(x,a-1,x,y); y=merge(ch[y][0],ch[y][1]); root=merge(merge(x,y),z); } else if(p==3) { split(root,a-1,x,y); printf("%d\\n",size[x]+1); root=merge(x,y); } else if(p==4) printf("%d\\n",val[kth(root,a)]); else if(p==5) { split(root,a-1,x,y); printf("%d\\n",val[kth(x,size[x])]); root=merge(x,y); } else { split(root,a,x,y); printf("%d\\n",val[kth(y,1)]); root=merge(x,y); } } return 0; }
二:区间操作
区间提取
分为两次split操作 第一次split(root,pos-1,x1,x2);第二次split(x2,len,y1,y2) y1就是提取出的区间 注意split是按照编号split而不是权值
因此就可以进行一系列的区间操作,如区间改值,区间删除,区间插入等
bzoj1500 维修数列 http://www.lydsy.com/JudgeOnline/problem.php?id=1500
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define maxn 500010 #define inf 0x3f3f3f3f using namespace std; int siz[maxn],ch[maxn][2],fix[maxn],val[maxn],a[maxn],f[maxn]; int tmx[maxn],lmx[maxn],rmx[maxn],sum[maxn],rev[maxn],cov[maxn]; int T,cnt,n,m,b,root,com; queue<int>trashcan; inline int read() { int x=0,f=1;char c=getchar(); while(c>\'9\'||c<\'0\'){if(c==\'-\')f=-1;c=getchar();} while(c>=\'0\'&&c<=\'9\'){x=x*10+c-\'0\';c=getchar();} return x*f; } long int random() { static int seed=703; return seed=int(seed*48271LL%2147483647); } inline int max(int x,int y){return x>y?x:y;} inline int min(int x,int y){return x<y?x:y;} void swap(int &a,int &b){a^=b;b^=a;a^=b;} int new_node(int v) { int x; if(!trashcan.empty()) x=trashcan.front(),trashcan.pop(); else x=++cnt; ch[x][0]=ch[x][1]=rev[x]=0; cov[x]=inf;fix[x]=random();siz[x]=1; val[x]=sum[x]=tmx[x]=v; lmx[x]=rmx[x]=max(v,0); return x; } void update(int x) { if(ch[x][0]&&ch[x][1]) { siz[x]=1+siz[ch[x][0]]+siz[ch[x][1]]; sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x]; tmx[x]=max(tmx[ch[x][0]],tmx[ch[x][1]]); tmx[x]=max(tmx[x],rmx[ch[x][0]]+val[x]+lmx[ch[x][1]]); lmx[x]=max(lmx[ch[x][0]],sum[ch[x][0]]+val[x]+lmx[ch[x][1]]); rmx[x]=max(rmx[ch[x][1]],sum[ch[x][1]]+val[x]+rmx[ch[x][0]]); } else if(ch[x][0]) { siz[x]=siz[ch[x][0]]+1; sum[x]=sum[ch[x][0]]+val[x]; tmx[x]=max(tmx[ch[x][0]],rmx[ch[x][0]]+val[x]); lmx[x]=max(lmx[ch[x][0]],sum[ch[x][0]]+val[x]); lmx[x]=max(0,lmx[x]); rmx[x]=max(0,val[x]+rmx[ch[x][0]]); } else if(ch[x][1]) { siz[x]=siz[ch[x][1]]+1; sum[x]=sum[ch[x][1]]+val[x]; tmx[x]=max(tmx[ch[x][1]],lmx[ch[x][1]]+val[x]); rmx[x]=max(rmx[ch[x][1]],sum[ch[x][1]]+val[x]); rmx[x]=max(0,rmx[x]); lmx[x]=max(0,lmx[ch[x][1]]+val[x]); } else { siz[x]=1,sum[x]=tmx[x]=val[x]; lmx[x]=rmx[x]=max(val[x],0); } } void reverse(int x) { swap(ch[x][0],ch[x][1]); swap(lmx[x],rmx[x]); rev[x]^=1; } void cover(int x,int v) { val[x]=v;sum[x]=siz[x]*v; lmx[x]=rmx[x]=max(sum[x],0); tmx[x]=max(sum[x],val[x]); cov[x]=v; } void pushdown(int x) { if(rev[x]) { if(ch[x][0]) reverse(ch[x][0]); if(ch[x][1]) reverse(ch[x][1]); } if(cov[x]!=inf) { if(ch[x][0]) cover(ch[x][0],cov[x]); if(ch[x][1]) cover(ch[x][1],cov[x]); } rev[x]=0;cov[x]=inf; } int build(int *data,int n) { int x,last=0;static int sta[maxn],top; for(int i=1;i<=n;i++) { x=new_node(data[i]),last=0; while(top&&fix[sta[top]]>fix[x]) update(sta[top]),last=sta[top],sta[top--]=0; if(top) ch[sta[top]][1]=x; ch[x][0]=last;sta[++top]=x; } while(top) update(sta[top--]); return sta[1]; } void split(int now,int k,int &x,int &y) { if(!now) x=y=0; else { pushdown(now); if(siz[ch[now][0]]>=k) y=now,split(ch[now][0],k,x,ch[now][0]); else x=now,split(ch[now][1],k-siz[ch[now][0]]-1,ch[now][1],y); update(now); } } int merge(int A,int B) { if(A) pushdown(A); if(B) pushdown(B); if(A*B==0) return A+B; if(fix[A]<fix[B]){ch[A][1]=merge(ch[A][1],B);update(A);return A;} else{ch[B][0]=merge(A,ch[B][0]);update(B);return B;} } void trash(int x) { if(!x) return; trashcan.push(x); trash(ch[x][0]); trash(ch[x][1]); } void insert() { int pos=read(),len=read(),x,y; static int datas[maxn]; for(int i=1;i<=len;i++) datas[i]=read(); int rt=build(datas,len); split(root,pos,x,y); root=merge(merge(x,rt),y); } void del() { int pos=read(),len=read(),x1,x2,y1,y2; split(root,pos-1,x1,x2); split(x2,len,y1,y2); root=merge(x1,y2); trash(y1); } void covers() { int pos=read(),len=read(),v=read(),x1,x2,y1,y2; split(root,pos-1,x1,x2); split(x2,len,y1,y2); cover(y1,v); root=merge(x1,merge(y1,y2)); } void reverses() { int pos=read(),len=read(),x1,x2,y1,y2; split(root,pos-1,x1,x2); split(x2,len,y1,y2); reverse(y1); root=merge(x1,merge(y1,y2)); } void sums() { int pos=read(),len=read(),x1,x2,y1,y2; split(root,pos-1,x1,x2); split(x2,len,y1,y2); printf("%d\\n",sum[y1]); root=merge(x1,merge(y1,y2)); } int main() { n=read();m=read(); for(int i=1;i<=n;i++) a[i]=read(); root=build(a,n); static char s[20]; while(m--) { scanf("%s",s); if(s[0]==\'I\') insert(); else if(s[0]==\'D\') del(); else if(s[0]==\'M\'&&s[2]==\'K\') covers(); else if(s[0]==\'R\') reverses(); else if(s[0]==\'G\') sums(); else printf("%d\\n",tmx[root]); } return 0; }
以上是关于可持久化Treap(fhq Treap,非旋转式Treap)学习(未完待续)的主要内容,如果未能解决你的问题,请参考以下文章