P3369 【模板】普通平衡树

P3369 【模板】普通平衡树

splay模板

优质讲解

我只是发一个模板...

我太弱了讲不清

注意操作时可能会访问到最小值的前驱或最大值的后继

所以要多加入两个虚节点INF和 -INF防止越界

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=1e5+7;
inline int read()
{
    register int x=0,f=1;
    char ch=getchar();
    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();
    }
    return x*f;
}
int n;

//------------------------------Splay--------------------------------
int ch[N<<2][2],sz[N<<2],fa[N<<2],val[N<<2],num[N<<2],cnt,rt;
inline void pushup(int x){ sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+num[x]; }//更新当前节点
inline void rotate(int x,int &k)//伸展
{
    int y=fa[x],z=fa[y],d=(ch[y][1]==x);
    if(y==k) k=x;
    else ch[z][(ch[z][1]==y)]=x;
    fa[x]=z; fa[y]=x; fa[ch[x][d^1]]=y; 
    ch[y][d]=ch[x][d^1]; ch[x][d^1]=y;
    pushup(y); pushup(x);
}
inline void splay(int x,int &k)//splay
{
    while(x!=k)
    {
        int y=fa[x],z=fa[y];
        if(y!=k)
        {
            if( (ch[y][1]==x)^(ch[z][1]==y) ) rotate(x,k);
            else rotate(y,k);
        }
        rotate(x,k);
    }
}
inline void find(int x)//找到数x,并将它弄到树根
{
    int now=rt;
    while(ch[now][ x>val[now] ] && x!=val[now]) now=ch[now][ x>val[now] ];
    splay(now,rt);
}
inline int Q_kth(int k)//询问第k大
{
    int now=rt;
    while(233)
    {
        if(ch[now][0]&&k<=sz[ch[now][0]]) { now=ch[now][0]; continue; }
        if(k>sz[ ch[now][0] ]+num[now] )
        {
            k-=sz[ ch[now][0] ]+num[now];
            now=ch[now][1];
            continue;
        }
        return now;
    }
}
inline void Q_rank(int x)//询问x的排名
{
    find(x);//把它转到树根,然后输出左子树的大小就好了(左边都比它小,右边都比它大)
    printf("%d\n",sz[ch[rt][0]]/*注意不用加1,因为有多一个虚节点-INF在左边*/);
}
inline int pre(int x)//询问前驱,把节点转到根然后输出左子树中的最大值(即左子树最右边的节点)
{
    find(x);
    if(val[rt]<x) return rt;//特判一下
    int now=ch[rt][0];//左子树
    while(ch[now][1]) now=ch[now][1];//一直往右走
    return now;
}
inline int nex(int x)//同理
{
    find(x);
    if(val[rt]>x) return rt;
    int now=ch[rt][1];
    while(ch[now][0]) now=ch[now][0];
    return now;
}
inline void ins(int x)//插入节点
{
    int now=rt,f=0;
    while(now&&val[now]!=x)//先找到位置
    {
        f=now;
        now=ch[now][ x>val[now] ];
    }
    if(now) num[now]++,sz[now]++;//如果已经有值就直接更新
    else//否则就增加节点
    {
        now=++cnt;
        if(f) ch[f][ x>val[f] ]=now;
        val[now]=x; fa[now]=f;
        num[now]=sz[now]=1;
    }
    splay(now,rt);//最后要Splay一波来更新整颗树的状态
}
inline void erase(int x)//删除节点,把前驱转到根,后继转到根的右儿子,那么后继的左子树有且只有当前节点
{
    int now=nex(x);
    splay(pre(x),rt); splay(now,ch[rt][1]);
    now=ch[now][0];
    if(num[now]>1)//如果不止一个就减1
        num[now]--,sz[now]--;
    else ch[fa[now]][0]=0;//否则就删除节点
    pushup(fa[now]);
    pushup(fa[fa[now]]);
}
//------------------------------------------------------------

int main()
{
    n=read();
    ins(0x3f3f3f3f); ins(0xcfcfcfcf);//插入虚节点
    int a,b;
    for(int i=1;i<=n;i++)
    {
        a=read(); b=read();
        if(a==1) ins(b);
        if(a==2) erase(b);
        if(a==3) Q_rank(b);
        if(a==4) printf("%d\n",val[ Q_kth(b+1/*注意+1,因为有虚节点*/) ]);
        if(a==5) printf("%d\n",val[ pre(b) ]);
        if(a==6) printf("%d\n",val[ nex(b) ]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/LLTYYC/p/9770678.html