Splay 平衡树模板

OI-wiki写的非常好,所以在这里加以自己的注释理解存一下模板qwq。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i<(b);++i)
#define rrep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
template <typename T>
inline void read(T &x){
    x=0;char ch=getchar();bool f=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    if(f)x=-x;
}
template <typename T,typename ...Args>
inline void read(T &tmp,Args &...tmps){read(tmp);read(tmps...);}
const int N = 1e5 + 5;
int root,tot,fa[N],ch[N][2],val[N],cnt[N],siz[N];
struct Splay{
    void maintain(int x){
        siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + cnt[x];
    }//pushup
    bool get(int x){return x == ch[fa[x]][1];}//是爹的左儿子(0)还是右儿子(1)
    void clear(int x){//销毁结点
        ch[x][0] = ch[x][1] = fa[x] = val[x] = siz[x] = cnt[x] = 0;
    }
    void rotate(int x){//旋转操作
        int y = fa[x],z = fa[y],chk = get(x);//爹,爹的爹,是爹的哪个儿子
        ch[y][chk] = ch[x][chk^1];//如果我是爹的左儿子,把我的右儿子给爹的左儿子//如果是右儿子,把我的左儿子给爹的右儿子
        if(ch[x][chk^1])fa[ch[x][chk^1]] = y;//把这个儿子的爹改成我的爹
        ch[x][chk^1] = y;//父子关系换了,哈哈!
        fa[y] = x;fa[x] = z;//哈哈!哈哈!
        if(z)ch[z][y == ch[z][1]] = x;//如果爹的爹存在,更新儿子,你滴儿子是我辣!
        maintain(x);maintain(y);//pushup pushup
    }
    void splay(int x){
        for(int f;f = fa[x];rotate(x))//我还有爹吗,有就旋
        if(fa[f])rotate(get(x) == get(f) ? f : x);//如果有爹,相同的话要先旋爹
        root = x;//我是根辣!
    }
    void ins(int k){
        if(!root){
            val[++tot] = k;
            cnt[tot]++;
            root = tot;
            maintain(root);return;//空就新建结点
        }
        int cur = root,f = 0;
        while(1){
            if(val[cur] == k){//这个数存在那么就cnt+1就好了
                cnt[cur]++;
                maintain(cur);maintain(f);
                splay(cur);//别忘了
                break;
            }
            f = cur;
            cur = ch[cur][val[cur] < k];//当前节点比k小就往右,否则往左
            if(!cur){//走不下去了,就新建节点,完事!
                val[++tot] = k;
                cnt[tot]++;
                fa[tot] = f;
                ch[f][val[f] < k] = tot;//别忘了爹的儿子
                maintain(tot);maintain(f);splay(tot);break;
            }
        }
    }
    int rk(int k){
        int res = 0,cur = root;
        while(1){
            if(k < val[cur])cur = ch[cur][0];//当前节点大了就往左查
            else{
                res += siz[ch[cur][0]];//否则加上左子树的大小,注意不加自己
                if(k == val[cur]){//如果刚好查到了那么就是返回答案了,比他小的个数 + 1
                    splay(cur);
                    return res + 1;
                }       
                res += cnt[cur];//比当前结点大,那么加上当前结点继续右子树
                cur = ch[cur][1];//右子树
            }
            if(!cur)return res + 1;//这里一定要特判!!不然过不了数据加强版,害我调半天!!!
        }
    }
    int kth(int k){
        int cur = root;
        while(1){
            if(ch[cur][0] && k <= siz[ch[cur][0]])cur = ch[cur][0];//左儿子够
            else{
                k -= siz[ch[cur][0]] + cnt[cur];//权值线段树啦~~
                if(k <= 0){//够了就是当前结点了
                    splay(cur);return val[cur];
                }
                cur = ch[cur][1];//不够往右跳
            }
        }
    }
    int pre(){
        int cur = ch[root][0];//前驱,就是先插入这个数,把他旋到根,那么就是他左儿子的右儿子最深那个,也就是最大的小于他的
        if(!cur)return cur;//不存在
        while(ch[cur][1])cur = ch[cur][1];//跳最深的右儿子
        splay(cur);//嗯,别忘了!
        return cur;
    }
    int nxt(){
        int cur = ch[root][1];//后继同理
        if(!cur)return cur;
        while(ch[cur][0])cur = ch[cur][0];//不过跳右子树的左儿子
        splay(cur);//咳咳,别忘了!
        return cur;
    }
    void del(int k){
        rk(k);//就是把他的结点扔到根
        if(cnt[root] > 1){//有一堆,删一个就好了
            cnt[root]--;
            maintain(root);
            return;
        }
        if(!ch[root][0] && !ch[root][1]){//这棵树只有他一个
            clear(root);//那么就删掉了
            root = 0;
            return ;
        }
        if(!ch[root][0]){//只有右儿子
            int cur = root;
            root = ch[root][1];//删掉就好了,更新一下右儿子的父亲
            fa[root] = 0;
            clear(cur);
            return;
        }
        if(!ch[root][1]){
            int cur = root;
            root = ch[root][0];//左边同理
            fa[root] = 0;
            clear(cur);
            return;
        }
        int cur = root;//最麻烦的!
        int x = pre();//先找到前驱
        fa[ch[cur][1]] = x;//右儿子挂到前驱下
        ch[x][1] = ch[cur][1];//前驱的右儿子是他的右儿子
        clear(cur);//删了就好了
        maintain(root);
    }
}tree;
signed main(){
    int n,opt,x;
    read(n);
    while(n--){
        read(opt,x);
        if(opt == 1)tree.ins(x);
        else if(opt == 2)tree.del(x);
        else if(opt == 3)printf("%d\n",tree.rk(x));
        else if(opt == 4)printf("%d\n",tree.kth(x));
        else if(opt == 5)tree.ins(x),printf("%d\n",val[tree.pre()]),tree.del(x);
        else tree.ins(x),printf("%d\n",val[tree.nxt()]),tree.del(x);
    }
}
posted @ 2022-09-05 19:18  Xu_brezza  阅读(89)  评论(2编辑  收藏  举报