替罪羊树模板

替罪羊树要记一下几个东西:

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值