普通平衡树 lg3369

Posted wenci

tags:

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

 在多次学习splay后,我终于理解并码出了整份代码

参考了https://tiger0132.blog.luogu.org/slay-notes的博客

具体实现原理在上面这篇博客和百度中可以查到,接下来我们看一下代码

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define ls st[p].ch[0]
#define rs st[p].ch[1]
inline int read(){
    int w=0,f=1;
    char ch=getchar();
    while(ch<0||ch>9){
        if(ch==-) f=-1;
        ch=getchar();
    }
    while(ch>=0&&ch<=9){
        w=(w<<3)+(w<<1)+ch-48;
        ch=getchar();
    }
    return w*f;
} 
int n,m,cnt,tot,root;
struct node{
    int val,cnt,sum,ch[2],f;
}st[1000010];//val表示当前值,cnt表示出现次数,sum表示包括自己在内的子树大小,ch[0]为左儿子,ch[1]为右儿子
inline void push_up(int x){
    st[x].sum=st[st[x].ch[0]].sum+st[st[x].ch[1]].sum+st[x].cnt;
}//上推,子树大小为左子树加右子树加自身
inline void connect(int x,int fa,int son){
    st[x].f=fa;st[fa].ch[son]=x;
}//重新连接父子节点
inline bool identify(int x){
    return st[st[x].f].ch[1]==x;
}//判断自己是左儿子还是右儿子
inline void rotate(int x){
    int y=st[x].f;int z=st[y].f;//父亲和曾祖父
    int yson=identify(x);int zson=identify(y);//身份认定
    int b=st[x].ch[yson^1];//往上连接的节点一定是st[x].ch[yson^1]可以手画几张图推一下情况
    connect(b,y,yson);connect(y,x,(yson^1));connect(x,z,zson);
    push_up(y);push_up(x);return;
}//rotate的实现原理在上面那个博客里有详细介绍
inline void splay(int x,int goal){
    while(st[x].f!=goal){
        int y=st[x].f;int z=st[y].f;
        int yson=identify(x);int zson=identify(y);
        if(z!=goal){
            if(yson==zson) rotate(y);
            else rotate(x);
        }
        rotate(x);
    }
    if(!goal) root=x;
    return;
}//splay操作就是将节点向目标不断旋转
inline void insert(int x){
    int now=root;int f=0;
    while(now&&st[now].val!=x){//原平衡树上不一定有现在查询的这个值,所以在往下跳的同时记录上一步,也就是父节点的位置
        f=now;
        now=st[now].ch[x>st[now].val];
    }
    if(now){
        st[now].cnt++;//如果查询到了,并且发现已经有这个节点,就cnt++;
    }
    else{
        tot++;now=tot;//如果发现原先没有这么个节点,就新建一个
        if(f){
            st[f].ch[x>st[f].val]=now;//从父亲连向儿子
        }
        st[now].ch[0]=st[now].ch[1]=0;//儿子连向父亲
        st[now].sum=st[now].cnt=1;
        st[now].f=f;st[now].val=x;
    }
    splay(now,0);return;//将当前节点旋转到根
}
inline void find(int x){
    int now=root;//本操作与上面的操作类似,找到当前值的节点,然后把其旋转到根上
    if(!now) return;
    while(st[now].ch[x>st[now].val]&&x!=st[now].val){
        now=st[now].ch[x>st[now].val];
    }
    splay(now,0);return;
}
inline int Next(int x,int f){//当f=0时查询的是前驱,f=1时查询的是后继
    find(x);int now=root;
    if(st[now].val<x&&!f) return now;
    if(st[now].val>x&&f) return now;
    now=st[now].ch[f];
    while(st[now].ch[f^1]) now=st[now].ch[f^1];
    return now;
}
inline void Delete(int x){//删除节点
    int la=Next(x,0);int ne=Next(x,1);//查询该点的前驱后继
    splay(la,0);splay(ne,la);//现将前驱转到根,再将后继转向前驱,此时后继的左儿子就是当前要删的节点了
    int now=st[ne].ch[0];
    if(st[now].cnt>1){//如果该点的cnt>1,cnt--即可,要不就把子节点的联系断掉就好了
        st[now].cnt--;
        splay(now,0);
    }
    else{
        st[ne].ch[0]=0;
    }
    return;
}
inline int k_th(int x){//查询第k大
    int now=root;
    if(st[now].sum<x) return false;//如果总数不够就大力返回FALSE;
    while(true){
        int lson=st[now].ch[0];//左儿子
        if(x>st[lson].sum+st[now].cnt){//如果x比左儿子的大小加上当前节点出现次数还大,就向右子树查询,记得减去之前的数值
            x-=st[lson].sum+st[now].cnt;
            now=st[now].ch[1];
        }
        else if(st[lson].sum>=x){
            now=lson;//如果x比左子树的大小小,就向左查询
        }
        else return st[now].val;//如果不属于以上两种情况就是在当前节点了
    }
}
int main(){//主函数就是根据题目要求大力操作了
    m=read();int i,j,k;root=0;
    insert(INF);insert(-INF);
    while(m--){
        int opt=read(),x=read();
        if(opt==1) insert(x);
        if(opt==2) Delete(x);
        if(opt==3){
            find(x);printf("%d
",st[st[root].ch[0]].sum);
        }
        if(opt==4){
            printf("%d
",k_th(x+1));
        }
        if(opt==5){
            printf("%d
",st[Next(x,0)].val);
        }
        if(opt==6){
            printf("%d
",st[Next(x,1)].val);
        }
    }
    return 0;
}

普通平衡树这道题大概就是这样了,接下来会跟进有关题目的训练及题解。

以上是关于普通平衡树 lg3369的主要内容,如果未能解决你的问题,请参考以下文章

P3369 模板普通平衡树 题解(Splay)

Luogu P3369 模板普通平衡树

[luogu3369][普通平衡树]

洛谷P3369普通平衡树(splay)

P3369 模板普通平衡树(Treap/SBT)

题解 P3369 模板普通平衡树(Treap/SBT)