#include<cstdio> #define MXN 100000+3 #define max(a,b) (a>b?a:b) #define min(a,b) (a<b?a:b) #define nil 0 #define LEFT false #define RIGHT true int val[MXN],size[MXN],fa[MXN],left[MXN],right[MXN],recycle[1001],root; int ntop,rtop=-1,rcount; int newnode(){ int nw; if(rtop!=-1) nw=recycle[rtop--]; else nw=++ntop; val[nw]=0; size[nw]=1; fa[nw]=nil; left[nw]=nil; right[nw]=nil; return nw; } int newnode(int k){ int nw=newnode(); val[nw]=k; return nw; } void rotate(int now){rcount++; if(fa[now]==nil) return; int f=fa[now],gf=fa[f],l=left[now],r=right[now]; fa[now]=gf; if(gf!=nil){ if(left[gf]==f) left[gf]=now; if(right[gf]==f) right[gf]=now; } if(left[f]==now){ right[now]=f; left[f]=r; if(r!=nil) fa[r]=f; } if(right[f]==now){ left[now]=f; right[f]=l; if(l!=nil) fa[l]=f; } fa[f]=now; size[f]=size[left[f]]+size[right[f]]+1; size[now]=size[left[now]]+size[right[now]]+1; if(fa[now]==nil) root=now; return; } void maintain(int now,bool type){ int l=left[now],r=right[now],b=nil,rt; if(type==LEFT){ if(size[left[l]]>size[r]){ rotate(l); rt=l; } else if(size[right[l]]>size[r]){ b=right[l]; rotate(b); rotate(b); rt=b; } else return; } if(type==RIGHT){ if(size[right[r]]>size[l]){ rotate(r); rt=r; } else if(size[left[r]]>size[l]){ b=left[r]; rotate(b); rotate(b); rt=b; } else return; } maintain(left[rt],LEFT); maintain(right[rt],RIGHT); maintain(rt,LEFT); maintain(rt,RIGHT); return; } int search(int now,int k){ if(now==nil) return nil; if(val[now]==k) return now; if(val[now]>k) return search(left[now],k); if(val[now]<k) return search(right[now],k); } int insert(int &now,int ins){ if(root==nil) root=ins; else if(now==nil) now=ins; else{ ++size[now]; if(val[ins]<=val[now]){ if(left[now]==nil){ left[now]=ins; fa[ins]=now; } else insert(left[now],ins); } else{ if(right[now]==nil){ right[now]=ins; fa[ins]=now; } else insert(right[now],ins); } maintain(now,val[ins]>val[now]); } return ins; } void remove(int now,int k){ if(now==nil) return; if(val[now]==k){ if(left[now]==nil&&right[now]==nil){ if(left[fa[now]]==now) left[fa[now]]=nil; else right[fa[now]]=nil; fa[now]=nil; recycle[++rtop]=now; return; } else if(left[now]==nil){ if(left[fa[now]]==now) left[fa[now]]=right[now]; else right[fa[now]]=right[now]; fa[right[now]]=fa[now]; fa[now]=nil; recycle[++rtop]=now; return; } else if(right[now]==nil){ if(left[fa[now]]==now) left[fa[now]]=left[now]; else right[fa[now]]=left[now]; fa[left[now]]=fa[now]; fa[now]=nil; recycle[++rtop]=now; return; } else{ int t=left[now]; while(right[t]!=nil) t=right[t]; val[now]=val[t]; size[now]--; remove(left[now],val[t]); return; } } else{ size[now]--; if(val[now]>k) remove(left[now],k); else remove(right[now],k); } return; } int select(int now,int k){ if(now==nil) return nil; if(size[left[now]]+1==k) return now; if(size[left[now]]+1>=k) return select(left[now],k); if(size[left[now]]+1<k) return select(right[now],k-size[left[now]]-1); } int rank(int now,int k){ if(now==nil) return 1; if(val[now]>=k) return rank(left[now],k); if(val[now]<k) return rank(right[now],k)+size[left[now]]+1; } int get_prev(int now,int k){ if(now==nil) return -0x6fffffff; if(val[now]<k) return max(val[now],get_prev(right[now],k)); else return get_prev(left[now],k); } int get_succ(int now,int k){ if(now==nil) return 0x6fffffff; if(val[now]>k) return min(val[now],get_succ(left[now],k)); else return get_succ(right[now],k); } int main(){ int n; scanf("%d",&n); root=nil; int p,x; while(n--){ scanf("%d%d",&p,&x); if(p==1) insert(root,newnode(x)); if(p==2) remove(root,x); if(p==3) printf("%d\n",rank(root,x)); if(p==4) printf("%d\n",val[select(root,x)]); if(p==5) printf("%d\n",get_prev(root,x)); if(p==6) printf("%d\n",get_succ(root,x)); } return 0; }
子树大小平衡树是一种平衡树,利用子树大小维护平衡。满足下面性质的二叉搜索树称为SBT:
除根外,每棵树的大小不小于其兄弟的儿子的大小。
性质十分简单。由于具有对称性(即若L和R互为兄弟,则L大小不小于R的儿子大小,同时R大小不小于L的儿子大小),因此可以使树保持平衡。
SBT基本操作为旋转。旋转与splay定义不同(貌似与很多平衡树相同),左旋一棵树指使其右子树的根成为这棵树新的根,右旋与左旋对称。
主要的维护利用maintain,分以下四种情况:
1、若某点右子树大小小于其左儿子的左子树大小,则右旋其本身,对称的情况是某点左子树大小小于其右儿子的右子树大小,则左旋其本身;
2、若某点右子树大小小于其左儿子的右子树大小,则先左旋其左子树,再右旋其本身,另一种情况与这种情况对称。
旋转之后,有一些树的大小发生变化,那么需要继续maintain,直到满足SBT性质。若上述maintain了子树A,则先maintainA的左右子树,再maintainA本身。
有一种对maintain的改良方法,即maintain时只关注某一个子树是否小于其兄弟的儿子。不过我并不感觉改良了多少,大概改良了maintain次数,不过只是把四种情况分开讨论,原本四种情况也不会重叠。
复杂度还是很优的,时间相当的情况下旋转次数应比splay少(只会这两种平衡树...)。