替罪羊树学习笔记
替罪羊树是一种平衡树。然而它既不能可持久化,又不能维护区间。
所以把它发明出来干嘛?
然而它可以维护子树内信息。这个是一众平衡树都不能做到的功能。
它维护平衡的方式和普通的平衡树疯狂旋转不太一样。它是如果这个子树不平衡就把这个子树拍平了重构。
具体地,如果左子树的节点数和右子树的节点数比值超过某个值或小于某个值,那么就把这颗子树的中序遍历求出来,然后每次提取当前中序遍历中点,左儿子是左边中点,右儿子是右边中点。递归建树即可。
另外它的删除也和其它平衡树不太一样。它是惰性删除。即打一个标记然后在重构的时候再删。所以在求排名和kth时要注意当前点是否为空节点。
注意要依据
没了,其它就和普通BST差不多了。时间复杂度\(O(nlogn)\),常数还蛮小的。
放一下洛谷上模板的code:
#include<cstdio>
#define db double
#define l(x) f[x].l
#define r(x) f[x].r
#define alpha 0.7
using namespace std;
int n,m,k,x,y,z,cnt,root,op;
struct tree{int l,r,f,siz,w,sum;}f[100039];
int st[100039],sh;
inline int newnode(int x){f[++cnt]=(tree){0,0,1,1,1,x};return cnt;}
inline void dfs(int x){
if(!x) return;
dfs(l(x));if(f[x].w)st[++sh]=x;dfs(r(x));
}
inline void up (int x){f[x].f=f[l(x)].f+f[r(x)].f+1;f[x].siz=f[l(x)].siz+f[r(x)].siz+f[x].w;}
inline int build(int l=1,int r=sh){
if(r<l) return 0;int m=l+r>>1;
f[st[m]].l=build(l,m-1);f[st[m]].r=build(m+1,r);up(st[m]);return st[m];
}
inline void make(int &x){sh=0;dfs(x);x=build();}
inline int check(int x){return f[l(x)].f<f[r(x)].f*alpha||f[r(x)].f<f[l(x)].f*alpha;}
inline void get(int x,int &now){
if(!now){now=newnode(x);return;}
if(f[now].sum==x){f[now].w++;up(now);return;}
(x>f[now].sum)?get(x,r(now)):get(x,l(now));
up(now);if(check(now)) make(now);
}
inline void del(int x,int &now){
if(f[now].sum==x){f[now].w--;up(now);return;}
(x>f[now].sum)?del(x,r(now)):del(x,l(now));
up(now);if(check(now)) make(now);
}
inline int rk(int x,int &now){
if(!now) return 0;
if(x==f[now].sum) return f[l(now)].siz+f[now].w;
return (x>f[now].sum)?(f[now].w+f[l(now)].siz+rk(x,r(now))):(rk(x,l(now)));
}
inline int kth(int x,int &now){
if(f[l(now)].siz<x&&x<=f[l(now)].siz+f[now].w) return f[now].sum;
return (f[l(now)].siz>=x)?kth(x,l(now)):kth(x-f[l(now)].siz-f[now].w,r(now));
}
int main(){
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
register int i;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d%d",&op,&x);
if(op==1) get(x,root);
if(op==2) del(x,root);
if(op==3) printf("%d\n",rk(x-1,root)+1);
if(op==4) printf("%d\n",kth(x,root));
if(op==5) printf("%d\n",kth(rk(x-1,root),root));
if(op==6) printf("%d\n",kth(rk(x,root)+1,root));
}
}