关于平衡线段树的一点研究
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于平衡线段树的一点研究相关的知识,希望对你有一定的参考价值。
平衡树可以维护带插入删除的序列操作,加上一些线段树的特性即可像线段树一样维护区间信息
同样,线段树可以维护区间信息,加上一些平衡树的特性也就可以实现插入删除
不知为何似乎没见过什么相关资料所以就自己yy了一下
考虑旋转:
可以发现每次旋转只需更新一个区间的信息
定义每个节点的size为子树中叶节点个数
考虑类似sbt的平衡规则,每个节点是平衡的,当且仅当其子节点的size不小于另一子节点的任一子节点的size
类似线段树,每个叶节点维护原序列,非叶结点维护区间
对于在第x个元素前插入元素,首先找到代表x的叶节点,将x换为一棵size=2的子树,其中右子节点为原来的x,左子节点为新插入元素
修改操作和区间查询类似线段树
删除操作很简单,因为删的是叶节点,可以将待删除节点和其父节点删去,兄弟节点补到原位置
每次插入/删除后要沿操作路径调整平衡以保证单次操作严格O(logn)
在以上几个操作中平衡线段树的常数明显小于splay,查询操作的常数与线段树没有明显差别,并且必要时可以支持严格复杂度的可持久化
当然类似替罪羊树/treap的平衡规则也是可以用的,但复杂度变为均摊/期望
splay的旋转规则不易推广至平衡线段树
于是有了裸题 bzoj1014 [JSOI2008]火星人prefix
#include<cstdio> #include<cstring> typedef unsigned long long u64; const int N=600050,P=53; int lc[N],rc[N],sz[N],p=1; u64 v[N],pp[N]; char str[N],op[64],ch[64]; inline void up(int w){ v[w]=v[lc[w]]*pp[sz[rc[w]]]+v[rc[w]]; } inline void lrot(int&w){ int u=lc[w]; lc[w]=rc[u]; rc[u]=w; v[u]=v[w]; sz[u]=sz[w]; up(w); sz[w]=sz[lc[w]]+sz[rc[w]]; w=u; } inline void rrot(int&w){ int u=rc[w]; rc[w]=lc[u]; lc[u]=w; v[u]=v[w]; sz[u]=sz[w]; up(w); sz[w]=sz[lc[w]]+sz[rc[w]]; w=u; } inline void chk_l(int&w){ int m=sz[rc[w]],l=sz[lc[lc[w]]],r=sz[rc[lc[w]]]; if(m<l)lrot(w); else if(m<r)rrot(lc[w]),lrot(w); } inline void chk_r(int&w){ int m=sz[lc[w]],l=sz[lc[rc[w]]],r=sz[rc[rc[w]]]; if(m<r)rrot(w); else if(m<l)lrot(rc[w]),rrot(w); } void ins(int&w,int k){ ++sz[w]; if(sz[w]==2){ lc[w]=++p; v[p]=ch[0]-‘a‘+1; sz[p]=1; rc[w]=++p; v[p]=v[w]; sz[p]=1; up(w); }else if(k<sz[lc[w]]){ ins(lc[w],k); up(w); chk_l(w); }else{ ins(rc[w],k-sz[lc[w]]); up(w); chk_r(w); } } void chg(int&w,int k){ if(sz[w]==1){ v[w]=ch[0]-‘a‘+1; return; } if(k<=sz[lc[w]])chg(lc[w],k); else chg(rc[w],k-sz[lc[w]]); up(w); } int l,r; u64 ans=0; void get(int w,int L,int R){ if(l<=L&&R<=r){ ans=ans*pp[sz[w]]+v[w]; return; } int M=L+sz[lc[w]]; if(l<M)get(lc[w],L,M-1); if(r>=M)get(rc[w],M,R); } int build(int L,int R){ int w=++p; if(L==R){ v[w]=str[L]-‘a‘+1; sz[w]=1; return w; } int M=L+R>>1; sz[w]+=sz[lc[w]=build(L,M)]; sz[w]+=sz[rc[w]=build(M+1,R)]; up(w); return w; } inline int input(){ int x=0,c=getchar(); while(c>57||c<48)c=getchar(); while(c>47&&c<58)x=x*10+c-48,c=getchar(); return x; } inline int getc(){ int c=getchar(); while(c<=32)c=getchar(); return c; } int main(){ pp[0]=1; for(int i=1;i<100050;i++)pp[i]=pp[i-1]*P; scanf("%s",str+1); int n=strlen(str+1)+1,q,x,y; int rt=build(1,n); q=input(); while(q--){ op[0]=getc(); if(op[0]==‘Q‘){ x=input();y=input(); ans=0; int L=0,R=n-(x>y?x:y); while(L<R){ int M=L+R+1>>1; ans=0; l=x,r=x+M-1; if(l<=r)get(rt,1,n); u64 a1=ans; ans=0; l=y,r=y+M-1; if(l<=r)get(rt,1,n); if(a1!=ans)R=M-1; else L=M; } printf("%d\\n",L); }else if(op[0]==‘R‘){ x=input();ch[0]=getc(); chg(rt,x); }else{ x=input();ch[0]=getc(); ins(rt,x); ++n; } } return 0; }
以上是关于关于平衡线段树的一点研究的主要内容,如果未能解决你的问题,请参考以下文章