旋转Treap

treap是一种弱平衡的二叉搜索树,同时符合二叉搜索树和堆的性质,堆一般用随机值的小根堆。

旋转treap在每次有修改操作时,通过比较节点的优先值来决定是否旋转,通过旋转来维持树的平衡。

不维护自身副本数,左子树小于等于根节点,右子树严格大于根节点。

struct Treap{
    struct tree{
        int ch[2],v,p,w;
    }t[N];
    int root,tot;
    unsigned long long inf=1e10;
    #define l(p) (t[p].ch[0])
    #define r(p) (t[p].ch[1])
    #define ch(p,x) (t[p].ch[x])
    #define v(p) (t[p].v)
    #define w(p) (t[p].w)
    #define p(x) (t[x].p)
    inline int rand(){
        static default_random_engine x;
        static uniform_int_distribution<int>f(-1e8,1e8);
        return f(x);
    }
    inline int create(int x){
        t[++tot]={0,0,x,rand(),1};
        return tot;
    }
    inline void pushup(int p){
        w(p)=w(l(p))+w(r(p))+1;/*size包括自己*/
    }
    inline void rotate(int&p,int d){//d=0左旋,相当于与右孩子交换,d=1右旋,相当于与左孩子交换
        int x=ch(p,d^1);/*记录要变成根的子树*/
        ch(p,d^1)=ch(x,d);/*每一个值在记录后都立刻改变*/
        ch(x,d)=p;/*原根变为其子树*/
        pushup(p);
        p=x;
    }
    void insert(int&p,int x){
        if(!p){
            p=create(x);
            return;
        }
        int d=v(p)<x;/*x<=v(p)时递归左子树*/
        insert(ch(p,d),x);
        if(p(p)>p(ch(p,d)))rotate(p,d^1);/*若递归方向的子树优先级小于根节点将该节点旋转上来*/
        pushup(p);
    }
    void del(int&p,int x){
        if(!p)return;/*不存在节点则return*/
        if(v(p)==x){/*找到对应点*/
            if(!l(p)||!r(p)){/*只有一个节点直接删除即可*/
                p=l(p)|r(p);
                return;/*直接return,不再pushup*/
            }
            else{
                int d=p(l(p))<p(r(p));/*将优先级小的子节点旋转上来,根节点一直旋转到叶子节点进行删除*/
                rotate(p,d);/*与左孩子交换相当于右旋,与右孩子交换相当于左旋*/
                del(ch(p,d),x);/*递归转下去的根进行删除*/
            }
        }
        else del(ch(p,v(p)<x),x);/*x<=v(p)的情况递归左子树,否则递归右子树*/
        pushup(p);
    }
/*
递归写法
    int rank(int p,int x){
        if(!p)return 1;
        if(x<=v(p))return rank(l(p),x);
        else return rank(r(p),x)+w(l(p))+1;
    }
    int kth(int p,int x){
        if(!p)return inf;
        if(w(l(p))+1==x)return v(p);
        else if(x<=w(l(p)))return kth(l(p),x);
        else return kth(r(p),x-w(l(p))-1);
    }
*/
    inline int rank(int x){
        int p=root,re=1;/*初始化排名为1*/
        while(p){
            if(x<=v(p))p=l(p);/*查询最小排名,查到x时要递归左子树*/
            else re+=w(l(p))+1,p=r(p);/*加上左子树的大小*/
        }
        return re;
    }
    inline int kth(int x){
        int p=root;
        while(w(l(p))+1!=x){/*尚未找到相应排名*/
            if(x<=w(l(p)))p=l(p);/*对应的数在左子树*/
            else x-=w(l(p))+1,p=r(p);/*对应的数在右子树,更新排名*/
        }
        return v(p);
    }
    int pre(int p,int x){
        if(!p)return -inf;/*在所有小于x的点中取max*/
        if(v(p)<x)return max(v(p),pre(r(p),x));/*当前点已经小于x,在右子树中找介于当前点和x之间的数*/
        else return pre(l(p),x);/*当前点已经大于x,在左子树中介于当前点和x之间的数*/
    }
    int suc(int p,int x){
        if(!p)return inf;/*在所有大于x的点中取min*/
        if(v(p)>x)return min(v(p),suc(l(p),x));/*当前点已经大于x,在左子树中找介于当前点和x之间的数*/
        else return suc(r(p),x);/*当前点已经小于x,在右子树中找介于当前点和x之间的数*/
    }
    inline int rank(int x){
        return rank(root,x);
    }
    inline void insert(int x){
        insert(root,x);
    }
    inline void del(int x){
        del(root,x);
    }
    inline int pre(int x){
        return pre(root,x);
    }
    inline int suc(int x){
        return suc(root,x);
    }
};

每个节点维护一个副本数,左子树严格小于根节点,右子树严格大于根节点。

struct Treap{
    struct tree{
        int ch[2],v,p,w,c/*节点副本数*/;
    }t[N];
    int root,tot;
    unsigned long long inf=1e10;
    #define l(p) (t[p].ch[0])
    #define r(p) (t[p].ch[1])
    #define ch(p,x) (t[p].ch[x])
    #define v(p) (t[p].v)
    #define w(p) (t[p].w)
    #define p(x) (t[x].p)
    #define c(p) (t[p].c)
    inline int rand(){
        static default_random_engine x;
        static uniform_int_distribution<int>f(-1e8,1e8);
        return f(x);
    }
    inline int create(int x){
        t[++tot]={0,0,x,rand(),1,1};
        return tot;
    }
    inline void pushup(int p){
        w(p)=w(l(p))+w(r(p))+c(p);/*size包括自己的所有副本*/
    }
    inline void rotate(int&p,int d){//d=0左旋,相当于与右孩子交换,d=1右旋,相当于与左孩子交换
        int x=ch(p,d^1);/*记录要变成根的子树*/
        ch(p,d^1)=ch(x,d);/*每一个值在记录后都立刻改变*/
        ch(x,d)=p;/*原根变为其子树*/
        pushup(p);
        p=x;
    }
    void insert(int&p,int x){
        if(!p){
            p=create(x);
            return;
        }
        if(v(p)==x){
            c(p)++;
            pushup(p);
            return;
        }
        int d=v(p)<x;/*x<v(p)时递归左子树*/
        insert(ch(p,d),x);
        if(p(p)>p(ch(p,d)))rotate(p,d^1);/*若递归方向的子树优先级小于根节点将该节点旋转上来*/
        pushup(p);
    }
    inline void del(int&p,int x){
        if(!p)return;/*不存在节点则return*/
        if(v(p)==x){/*找到对应点*/
            if(c(p)>1){/*副本数大于1可以直接减少副本*/
                c(p)--;
                pushup(p);
                return;
            }
            if(!l(p)||!r(p)){/*只有一个节点直接删除即可*/
                p=l(p)|r(p);
                return;/*直接return,不再pushup*/
            }
            else{
                int d=p(l(p))<p(r(p));/*将优先级小的子节点旋转上来,根节点一直旋转到叶子节点进行删除*/
                rotate(p,d);/*与左孩子交换相当于右旋,与右孩子交换相当于左旋*/
                del(ch(p,d),x);/*递归转下去的根进行删除*/
            }
        }
        else del(ch(p,v(p)<x),x);/*x<=v(p)的情况递归左子树,否则递归右子树*/
        pushup(p);
    }
/*
递归写法
    int rank(int p,int x){
        if(!p)return 1;
        if(x<=v(p))return rank(l(p),x);
        else return rank(r(p),x)+w(l(p))+1;
    }
    int kth(int p,int x){
        if(!p)return inf;
        if(w(l(p))+1==x)return v(p);
        else if(x<=w(l(p)))return kth(l(p),x);
        else return kth(r(p),x-w(l(p))-1);
    }
*/
    inline int rank(int x){
        int p=root,re=1;/*初始化排名为1*/
        while(p){
            if(x<=v(p))p=l(p);/*查询最小排名,查到x时要递归左子树*/
            else re+=w(l(p))+1,p=r(p);/*加上左子树的大小*/
        }
        return re;
    }
    inline int kth(int x){
        int p=root;
        while(w(l(p))+1!=x){/*尚未找到相应排名*/
            if(x<=w(l(p)))p=l(p);/*对应的数在左子树*/
            else x-=w(l(p))+1,p=r(p);/*对应的数在右子树,更新排名*/
        }
        return v(p);
    }
    int pre(int p,int x){
        if(!p)return -inf;/*在所有小于x的点中取max*/
        if(v(p)<x)return max(v(p),pre(r(p),x));/*当前点已经小于x,在右子树中找介于当前点和x之间的数*/
        else return pre(l(p),x);/*当前点已经大于x,在左子树中介于当前点和x之间的数*/
    }
    int suc(int p,int x){
        if(!p)return inf;/*在所有大于x的点中取min*/
        if(v(p)>x)return min(v(p),suc(l(p),x));/*当前点已经大于x,在左子树中找介于当前点和x之间的数*/
        else return suc(r(p),x);/*当前点已经小于x,在右子树中找介于当前点和x之间的数*/
    }
    inline int rank(int x){
        return rank(root,x);
    }
    inline void insert(int x){
        insert(root,x);
    }
    inline void del(int x){
        del(root,x);
    }
    inline int pre(int x){
        return pre(root,x);
    }
    inline int suc(int x){
        return suc(root,x);
    }
}t;
posted @ 2022-11-14 18:04  半步蒟蒻  阅读(78)  评论(0)    收藏  举报