zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",val[ Q_kth(b+1/*注意+1,因为有虚节点*/) ]);
            if(a==5) printf("%d
    ",val[ pre(b) ]);
            if(a==6) printf("%d
    ",val[ nex(b) ]);
        }
        return 0;
    }
    奇怪的模板

    $Upd in one year later $

    上面那个链接真的只是优质讲解...模板就没那么优质了QAQ

    经过了最近几天的艰苦奋斗我终于意识到了这个模板的局限性....

    灵活性真的不行啊

    上面的模板把重复的值合并到一个节点里面,使得代码要多注意很多恶心的细节

    删除节点时因为可能会访问到边界外面所以要多插两个节点,使得细节更多...

    而且对于二逼平衡树这一题,如果用线段树套 $Splay$,那么每个线段树的节点的splay都要多来两个节点,那么总节点就又要多 $2nlog_n$ 个

    对于本来就十分卡线段树套$Splay$的这题来说更是难受,并且很容易因为细节问题出事,一开始用这个 $splay$ 写真的是要写疯掉了也写不出来,最后迫不得已只能重新学一份模板然后重构二逼平衡树的代码QAQ

    所以这里放一个比较灵活方便的不合并重复节点的 $Splay$,真的好写很多

    并且注意一下, $Splay$ 的复杂度理论上是均摊 $log_n$ 的

    但是不代表它一直都是平衡的,所以对于每个复杂度为树高的操作最后都要 $splay$ 一波来保证复杂度

    洛谷上这题只卡了求排名后不 $splay$ 的算法,其他操作很良心地没有卡

    所以我也只有查排名才 $splay$ 2333

    感觉平衡树也没什么好注释的了,反正大家都看得懂每个操作干什么2333

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        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;
    }
    const int N=2e5+7,INF=1e9+7;
    int n,ans;
    int rt,c[N][2],fa[N],sz[N],val[N],cnt;
    inline void pushup(int x) { sz[x]=sz[c[x][0]]+sz[c[x][1]]+1; }
    inline void rotate(int x,int &k)
    {
        int y=fa[x],z=fa[y],d=(c[y][1]==x);
        y==k ? k=x : c[z][c[z][1]==y]=x;
        fa[x]=z; fa[y]=x; fa[c[x][d^1]]=y;
        c[y][d]=c[x][d^1]; c[x][d^1]=y;
        pushup(y); pushup(x);
    }
    inline void splay(int x,int &k)
    {
        while(x!=k)
        {
            int y=fa[x],z=fa[y];
            if(y!=k)
            {
                if(c[y][0]==x ^ c[z][0]==y) rotate(x,k);
                else rotate(y,k);
            }
            rotate(x,k);
        }
    }
    inline void Q_rank(int k)
    {
        int x=rt,res=0,f=0;
        while(x)
        {
            f=x;
            if(val[x]>=k) x=c[x][0];
            else res+=sz[c[x][0]]+1,x=c[x][1];
        }
        if(f) splay(f,rt);
        printf("%d
    ",res+1);
    }
    inline int Q_kth(int x,int k)
    {
        while(233)
        {
            if(sz[c[x][0]]+1==k) return x;
            if(sz[c[x][0]]>=k) { x=c[x][0]; continue; }
            k-=sz[c[x][0]]+1; x=c[x][1];
        }//理论上此处要有splay
    }
    inline void ins(int k)
    {
        int x=rt,f=0;
        while(x) f=x,x=c[x][k>val[x]];
        x=++cnt; fa[x]=f; val[x]=k; sz[x]=1;
        if(f) c[f][k>val[f]]=x;
        if(rt) splay(x,rt);
        else rt=x;
    }
    inline void del(int k)//删除就把节点转到根然后把左右子树直接并起来
    {
        int x=rt; while(val[x]!=k) x=c[x][k>val[x]];
        splay(x,rt);
        if(c[x][0]&&c[x][1])
        {
            int y=Q_kth(rt,sz[c[x][0]]); splay(y,c[x][0]);
            x=rt; c[y][1]=c[x][1]; fa[c[x][1]]=y;
            rt=y; fa[rt]=0; pushup(y);
        }
        else if(c[x][0]) fa[c[x][0]]=0,rt=c[x][0];
        else if(c[x][1]) fa[c[x][1]]=0,rt=c[x][1];
        else rt=0;
    }
    void pre(int x,int k)//理论上前驱和后继跑完都要splay最底下的节点
    {
        if(!x) return;
        if(ans<val[x]&&k>val[x]) ans=val[x];
        pre(c[x][k>val[x]],k);
    }
    void nex(int x,int k)
    {
        if(!x) return;
        if(ans>val[x]&&k<val[x]) ans=val[x];
        nex(c[x][k>=val[x]],k);
    }
    int main()
    {
        n=read();
        int a,b;
        while(n--)
        {
            a=read(),b=read();
            if(a==1) { ins(b); continue; }
            if(a==2) { del(b); continue; }
            if(a==3) { Q_rank(b); continue; }
            if(a==4) { printf("%d
    ",val[Q_kth(rt,b)]); continue; }
            if(a==5) { ans=-INF; pre(rt,b); printf("%d
    ",ans);  continue; }
            ans=INF; nex(rt,b); printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    使用Python连接redis(redis作MQ使用)_Tister的空间_百度空间
    LTTng 2.0 Downloads | LTTng Project
    How to capture stdout in realtime with Python « SaltyCrane Blog
    python的线程锁机制_dominic_80ChinaUnix博客
    Collective Intelligence实战/阿拉克(Satnam Alag)图书卓越亚马逊 [集体智慧编程]
    查IP
    沃尔玛控股中国1号店
    HeidiSQL MySQL made easy
    groovy学习7groovy sql 雪霁霜飞 博客园
    数据集 (DataSet) groovy
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9770678.html
Copyright © 2011-2022 走看看