• 替罪羊树


    替罪羊树

    替罪羊树是一种不用旋转的平衡树,并且速度还不错,大约在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
    ", v[root]);
            for (int i=1; i<=cntn; ++i)
                printf("%d %d %d
    ", 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
    ", sgt.rank(sgt.root, x));
            if (op==4) printf("%d
    ", sgt.xth(sgt.root, x));
            if (op==5){
                int k=sgt.rank(sgt.root, x)-1;
                printf("%d
    ", sgt.xth(sgt.root, k));
            }
            if (op==6){
                int k=sgt.rank(sgt.root, x+1);
                printf("%d
    ", sgt.xth(sgt.root, k));
            }
            //sgt.printtree();
        }
        return 0;
    }
    
  • 相关阅读:
    LeetCode刷题之字符串
    LeetCode刷题之数组复习
    为什么要用移码来表示阶码(指数)呢?
    字节左移跟右移
    一个有趣的问题
    为什么对数组名取地址,得到的为整个数组的地址?
    在win10 64位下搭建汇编环境
    windows切换samba账号进行连接
    samba服务配置记录
    subversion钩子函数使用记录
  • 原文地址:https://www.cnblogs.com/MyNameIsPc/p/9062678.html
Copyright © 2020-2023  润新知