替罪羊树要记一下几个东西:
t:当前节点权值
cvr:子树当前大小(包括删除的点)
sz:子树中元素个数(不包括删除的点)
ex:当前元素是否存在
插入操作:
1. 插入一个数会导致一些其某些祖先满足要重构的条件,此时要取最浅的祖先直接重构。
2. insert要返回一个 tree ** 类型的东西,表示要重构的那个指针的地址。
3. 重构的时候(在travel)中要把已删除的点空间释放掉
4. vector里要存结构体的指针,假设程序其他部分有记录替罪羊树节点的东西,如果delete掉在new就会gg。
删除操作:
1. 删除操作是惰性删除
2. 删除途中因为树高只可能减少,所以复杂度有保证(可能不平衡,但也无需重构)。
3. 最后如果删除的点过多也要重构根节点。
还有判定是否重构的时候最后加一个小常数,避免一些靠近叶子的子树很容易满足重构条件。
然后因为替罪羊树没有旋转,只要定义好相等的数统一放在某一边子树,就可以兼容元素重复的问题。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const double alpha=0.75;
int n;
struct tree;
tree *NUL;
struct tree
{
int t,sz,cvr;
bool ex;
tree *s[2];
tree(){s[0]=s[1]=NUL;t=sz=cvr=0;ex=0;}
tree(int x){s[0]=s[1]=NUL;sz=cvr=1;ex=1;t=x;}
void update()
{
sz=s[0]->sz+s[1]->sz+ex;
cvr=s[0]->cvr+s[1]->cvr+1;
}
bool isbad()
{
return (s[0]->cvr>cvr*alpha+5)||(s[1]->cvr>cvr*alpha+5);
}
}*root;
void travel(tree *p,vector<tree *>&v)
{
if(p==NUL) return ;
travel(p->s[0],v);
if(p->ex) v.push_back(p);
travel(p->s[1],v);
if(!p->ex) delete p;
}
tree *divide(vector<tree *>&v,int l,int r)
{
if(l>=r) return NUL;
int mid=(l+r)>>1;
tree *p=v[mid];
p->s[0]=divide(v,l,mid);
p->s[1]=divide(v,mid+1,r);
p->update();
return p;
}
void rebuild(tree *&p)
{
static vector<tree *>v;v.clear();
travel(p,v);
p=divide(v,0,v.size());
}
tree ** insert(tree *&p,int val)
{
if(p==NUL) {p=new tree(val);return &NUL;}
else
{
p->sz++;p->cvr++;
tree **res=insert(p->s[val>=p->t],val);
if(p->isbad()) res=&p;
return res;
}
}
void erase(tree *&p,int id)
{
p->sz--;
int lsu=p->s[0]->sz+p->ex,b;
if(p->ex&&id==lsu) {p->ex=0;return ;}
b=id>lsu;
erase(p->s[b],id-b*lsu);
}
int rk(int val)
{
tree *p=root;
int re=1;
for(int b;p!=NUL;re+=b*(p->s[0]->sz+p->ex),p=p->s[b])
b=val>p->t;
return re;
}
int kth(int k)
{
tree *p=root;
for(int b,lsu;p!=NUL;k-=b*lsu,p=p->s[b])
{
lsu=p->s[0]->sz+p->ex;
if(p->ex&&k==lsu) return p->t;
b=(k>lsu);
}
}
void ins(int val)
{
tree **p=insert(root,val);
if(*p!=NUL) rebuild(*p);
}
void del(int k)
{
erase(root,rk(k));
if(root->sz<alpha*root->cvr) rebuild(root);
}
int main()
{
scanf("%d",&n);
NUL=new tree;NUL->s[0]=NUL->s[1]=NUL;root=NUL;
while(n--)
{
int opt,x;scanf("%d%d",&opt,&x);
if(opt==1) ins(x);
if(opt==2) del(x);
if(opt==3) printf("%d\n",rk(x));
if(opt==4) printf("%d\n",kth(x));
if(opt==5) printf("%d\n",kth(rk(x)-1));
if(opt==6) printf("%d\n",kth(rk(x+1)));
}
return 0;
}