替罪羊树

Posted PECHPO

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了替罪羊树相关的知识,希望对你有一定的参考价值。

替罪羊树

替罪羊树是一种不用旋转的平衡树,并且速度还不错,大约在treap和splay之间吧(treap就是那么快)。

这里用rank和xth操作实现了pre和next操作。注意rank操作找的是>=x且最小的编号,这样pre只需要查找rank(x)-1,next只需要查找rank(x+1),很优雅。

具体证明复杂度见知乎。(我不会啊)

#include <cstdio>
using namespace std;

const int maxn=1e5+5; const double alpha=0.7;
struct ScpGoatTree{  //v:元素值 cnt:个数 siz:子树大小
    int root, cntn, q[maxn], cntq;
    int l[maxn], r[maxn], v[maxn], cnt[maxn], siz[maxn];
    bool bad(int x){
        int t=siz[x]*alpha+5;
        if (siz[l[x]]>t||siz[r[x]]>t) return true;
        return false;
    }
    void clear(int x){ l[x]=r[x]=siz[x]=0; }
    void up(int x){ siz[x]=siz[l[x]]+siz[r[x]]+cnt[x]; }
    void dfs(int now){
        if (!now) return;
        dfs(l[now]);
        if (cnt[now]) q[++cntq]=now;
        dfs(r[now]);
    }
    void build(int &now, int L, int R){  //now被替换成区间中点
        if (L>R) return;
        int mid=(L+R)/2; now=q[mid]; clear(now); //一定要清空节点!以免访问到原有的左右孩子
        build(l[now], L, mid-1); build(r[now], mid+1, R);
        up(now);
    }
    void rebuild(int &now){ cntq=0; dfs(now); build(now, 1, cntq); }
    void modify(int &now, int x, int c){  //注意now是父节点的孩子的引用,因此可以直接改变
        if (!now) now=++cntn, v[now]=x;  //如果没有这个节点就先新建一个(只有插入操作可能触发)
        siz[now]+=c;
        if (x==v[now]){ cnt[now]+=c; return; }
        if (x<v[now]) modify(l[now], x, c);
        if (x>v[now]) modify(r[now], x, c);
        if (bad(now)) rebuild(now);
    }
    int rank(int now, int x){  //找>=x的点的排名。
        //如果要写<=x的点的排名,那么会不优雅
        int ans=0;
        while (now){
            if (x==v[now]) return ans+siz[l[now]]+1;
            if (x<v[now]) now=l[now];
            if (x>v[now]) ans+=siz[l[now]]+cnt[now], now=r[now];
        }
        return ans+1;
    }
    int xth(int now, int x){
        int need;
        while (now){
            need=x-siz[l[now]];  //除了左子树还需要的点
            if (need>0&&need<=cnt[now]) return v[now];
            if (need<=0) now=l[now];
            else x-=siz[l[now]]+cnt[now], now=r[now];
        }
        return -1;
    }
    void printtree(){
        printf("%d\n", v[root]);
        for (int i=1; i<=cntn; ++i)
            printf("%d %d %d\n", v[i], v[l[i]], v[r[i]]);
        puts("");
    }
}sgt;
int n, op, x;

int main(){
    scanf("%d", &n);
    for (int i=1; i<=n; ++i){
        scanf("%d%d", &op, &x);
        if (op==1) sgt.modify(sgt.root, x, 1);
        if (op==2) sgt.modify(sgt.root, x, -1);
        if (op==3) printf("%d\n", sgt.rank(sgt.root, x));
        if (op==4) printf("%d\n", sgt.xth(sgt.root, x));
        if (op==5){
            int k=sgt.rank(sgt.root, x)-1;
            printf("%d\n", sgt.xth(sgt.root, k));
        }
        if (op==6){
            int k=sgt.rank(sgt.root, x+1);
            printf("%d\n", sgt.xth(sgt.root, k));
        }
        //sgt.printtree();
    }
    return 0;
}

以上是关于替罪羊树的主要内容,如果未能解决你的问题,请参考以下文章

替罪羊树模版 普通平衡树

[数据结构]替罪羊树简介

平衡树合集(Treap,Splay,替罪羊,FHQ Treap)

[BZOJ3065]带插入区间K小值 解题报告 替罪羊树+值域线段树

几个平衡树板子

浅谈替罪羊树