zoukankan      html  css  js  c++  java
  • BZOJ 3224

    题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3224

    Description

    您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
    1. 插入x数
    2. 删除x数(若有多个相同的数,因只删除一个)
    3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
    4. 查询排名为x的数
    5. 求x的前驱(前驱定义为小于x,且最大的数)
    6. 求x的后继(后继定义为大于x,且最小的数)

    Input

    第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

    Output

    对于操作3,4,5,6每行输出一个数,表示对应答案

    Sample Input

    10

    1 106465

    4 1

    1 317721

    1 460929

    1 644985

    1 84185

    1 89851

    6 81968

    1 492737

    5 493598

    Sample Output

    106465

    84185

    492737

    HINT

    1.n的数据范围:n<=100000

    2.每个数的数据范围:[-2e9,2e9]

    题解1:

    Treap模板题。

    由于普通二叉搜索树容易退化成链状,考虑到在随机数据下产生的BST是趋近平衡树的,因此Treap就是用“随机”来创造平衡条件。

    在原来BST的基础上,我们可以对树上所有节点都另外增加一个随机生成的权值 $dat$,迫使整棵BST对于 $dat$ 满足“堆性质”。

    在Splay的学习中我们已经知道,右旋zig和左旋zag是不会影响BST的“BST性质”的,而通过zig和zag我们正好又可以达到交换父子节点位置的目的,

    因此,我们可以参照二叉堆,如果父子两节点不满足堆性质,则用zig或者zag交换两者位置,从而迫使整棵树满足关于 $dat$ 的“堆性质”。

    正是由于这颗二叉树,键值 $key$ 满足BST性质,另一个权值 $dat$ 满足堆性质,因此用单词 tree 和 heap 构成了该数据结构的名称 treap。

    AC代码(Treap):

    #include<bits/stdc++.h>
    using namespace std;
    const int INF=0x7fffffff;
    const int maxn=1e5+10;
    
    /******************************** Treap - st ********************************/
    int root,nodecnt;
    int ch[maxn][2];
    int key[maxn],dat[maxn];
    int cnt[maxn],siz[maxn];
    int NewNode(int val)
    {
        int x=++nodecnt;
        key[x]=val; dat[x]=rand();
        cnt[x]=siz[x]=1;
        ch[x][0]=ch[x][1]=0;
        return x;
    }
    void Pushup(int x)
    {
        siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+cnt[x];
    }
    void Init()
    {
        root=nodecnt=0;
        key[0]=dat[0]=0;
        cnt[0]=siz[0]=0;
        ch[0][0]=ch[0][1]=0;
    }
    void Build()
    {
        Init();
        NewNode(-INF);
        NewNode(INF);
        ch[root=1][1]=2;
        Pushup(root);
    }
    void zig(int &x)
    {
        int lc=ch[x][0];
        ch[x][0]=ch[lc][1], ch[lc][1]=x, x=lc;
        Pushup(ch[x][1]), Pushup(x);
    }
    void zag(int &x)
    {
        int rc=ch[x][1];
        ch[x][1]=ch[rc][0], ch[rc][0]=x, x=rc;
        Pushup(ch[x][0]), Pushup(x);
    }
    int GetRank(int x,int val)
    {
        if(x==0) return 0;
        if(val==key[x]) return siz[ch[x][0]]+1;
        if(val<key[x]) return GetRank(ch[x][0],val);
        return siz[ch[x][0]]+cnt[x]+GetRank(ch[x][1],val);
    }
    int GetKth(int x,int k)
    {
        if(x==0) return INF;
        if(siz[ch[x][0]]>=k) return GetKth(ch[x][0],k);
        if(siz[ch[x][0]]+cnt[x]>=k) return key[x];
        return GetKth(ch[x][1],k-siz[ch[x][0]]-cnt[x]);
    }
    void Insert(int &x,int val)
    {
        if(x==0)
        {
            x=NewNode(val);
            return;
        }
        if(val==key[x])
        {
            cnt[x]++;
            Pushup(x);
            return;
        }
        else if(val<key[x])
        {
            Insert(ch[x][0],val);
            if(dat[x] < dat[ch[x][0]]) zig(x);
        }
        else
        {
            Insert(ch[x][1],val);
            if(dat[x] < dat[ch[x][1]]) zag(x);
        }
        Pushup(x);
    }
    void Remove(int &x,int val)
    {
        if(x==0) return;
        if(val==key[x])
        {
            if(cnt[x]>1)
            {
                cnt[x]--;
                Pushup(x);
                return;
            }
            if(ch[x][0] || ch[x][1])
            {
                if(ch[x][1]==0 || dat[ch[x][0]] > dat[ch[x][1]]) zig(x), Remove(ch[x][1],val);
                else zag(x), Remove(ch[x][0],val);
                Pushup(x);
            }
            else x=0;
            return;
        }
        (val<key[x])?Remove(ch[x][0],val):Remove(ch[x][1],val);
        Pushup(x);
    }
    int GetPre(int val)
    {
        int ans=1; //a[1].val==-INF
        int x=root;
        while(x)
        {
            if(val==key[x])
            {
                if(ch[x][0]>0)
                {
                    x=ch[x][0];
                    while(ch[x][1]>0) x=ch[x][1];
                    ans=x;
                }
                break;
            }
            if(key[x]<val && key[x]>key[ans]) ans=x;
            x=(val<key[x])?ch[x][0]:ch[x][1];
        }
        return key[ans];
    }
    
    int GetNxt(int val)
    {
        int ans=2; //a[2].val==INF
        int x=root;
        while(x)
        {
            if(val==key[x])
            {
                if(ch[x][1]>0)
                {
                    x=ch[x][1];
                    while(ch[x][0]>0) x=ch[x][0];
                    ans=x;
                }
                break;
            }
            if(key[x]>val && key[x]<key[ans]) ans=x;
            x=(val<key[x])?ch[x][0]:ch[x][1];
        }
        return key[ans];
    }
    /******************************** Treap - ed ********************************/
    
    int main()
    {
        int n;
        cin>>n;
        Build();
        while(n--)
        {
            int opt,x;
            scanf("%d%d",&opt,&x);
            switch(opt)
            {
            case 1:
                Insert(root,x);
                break;
            case 2:
                Remove(root,x);
                break;
            case 3:
                printf("%d
    ",GetRank(root,x)-1);
                break;
            case 4:
                printf("%d
    ",GetKth(root,x+1));
                break;
            case 5:
                printf("%d
    ",GetPre(x));
                break;
            case 6:
                printf("%d
    ",GetNxt(x));
                break;
            }
        }
    }

     注:本模板的 $zig(x)$ 和 $zag(x)$ 中 $x$,是旋转前处于父亲节点位置。

    题解2:

    Splay模板题。

    Splay的原理以及实现参考:伸展树(Splay Tree)进阶 - 从原理到实现

    AC代码(Splay):

    #include<bits/stdc++.h>
    using namespace std;
    const int INF=0x7fffffff;
    const int maxn=1e5+10;
    
    /******************************** splay - st ********************************/
    #define Key_value ch[ch[root][1]][0]
    int root,nodecnt;
    int par[maxn],ch[maxn][2];
    int key[maxn],cnt[maxn],siz[maxn];
    void NewNode(int &x,int p,int k)
    {
        x=++nodecnt;
        par[x]=p;
        ch[x][0]=ch[x][1]=0;
        key[x]=k;
        cnt[x]=siz[x]=1;
    }
    void Pushup(int x)
    {
        siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+cnt[x];
    }
    void Rotate(int x,int type) //旋转,0为左旋zag,1为右旋zig
    {
        int y=par[x];
        ch[y][!type]=ch[x][type]; par[ch[x][type]]=y;
        if(par[y]) ch[par[y]][(ch[par[y]][1]==y)]=x;
        par[x]=par[y];
        ch[x][type]=y; par[y]=x;
        Pushup(y); Pushup(x);
    }
    void Splay(int x,int goal)
    {
        while(par[x]!=goal)
        {
            if(par[par[x]]==goal) Rotate(x,ch[par[x]][0]==x); //左孩子zig,右孩子zag
            else
            {
                int y=par[x];
                int type=(ch[par[y]][0]==y); //type=0,y是右孩子;type=1,y是左孩子
                if(ch[y][type]==x)
                {
                    Rotate(x,!type);
                    Rotate(x,type);
                }
                else
                {
                    Rotate(y,type);
                    Rotate(x,type);
                }
            }
        }
        if(goal==0) root=x;
    }
    int GetMin(int x)
    {
        while(ch[x][0]) x=ch[x][0];
        return x;
    }
    int GetMax(int x)
    {
        while(ch[x][1]) x=ch[x][1];
        return x;
    }
    void Init() //初始化,前后各加一个空节点
    {
        root=nodecnt=0;
        par[0]=ch[0][0]=ch[0][1]=0;
        cnt[0]=siz[0]=0;
        key[0]=0;
        NewNode(root,0,-INF); //头部加入一个空位
        NewNode(ch[root][1],root,INF); //尾部加入一个空位
        Pushup(ch[root][1]);
        Pushup(root);
    }
    
    void Insert(int val)
    {
        int x=root;
        while(1)
        {
            if(val==key[x])
            {
                cnt[x]++;
                Pushup(x);
                Splay(x,0);
                break;
            }
            int type=val>key[x];
            if(ch[x][type]==0)
            {
                NewNode(ch[x][type],x,val);
                Pushup(x);
                Splay(ch[x][type],0);
                break;
            }
            else x=ch[x][type];
        }
    }
    void Delete(int val)
    {
        int x=root;
        while(x)
        {
            if(val==key[x])
            {
                if(cnt[x]>1)
                {
                    cnt[x]--;
                    Pushup(x);
                    Splay(x,0);
                    return;
                }
                if(ch[x][0] && ch[x][1])
                {
                    Splay(GetMax(ch[x][0]),0);
                    Splay(GetMin(ch[x][1]),root);
                    par[Key_value]=0; Key_value=0;
                    Pushup(ch[root][1]); Pushup(root);
                    return;
                }
                int fa=par[x],type=(ch[fa][1]==x);
                if(ch[x][0])
                {
                    par[ch[x][0]]=fa;
                    ch[fa][type]=ch[x][0];
                    Pushup(fa);
                    Splay(ch[fa][type],0);
                    return;
                }
                if(ch[x][1])
                {
                    par[ch[x][1]]=fa;
                    ch[fa][type]=ch[x][1];
                    Pushup(fa);
                    Splay(ch[fa][type],0);
                    return;
                }
                ch[fa][type]=0;
                Pushup(fa);
                Splay(fa,0);
                return;
            }
            if(val<key[x]) x=ch[x][0];
            else x=ch[x][1];
        }
    }
    int GetRank_pos;
    int GetRank(int x,int val)
    {
        if(x==0) return 0;
        if(val==key[x]) return siz[ch[GetRank_pos=x][0]]+1;
        if(val<key[x]) return GetRank(ch[x][0],val);
        return siz[ch[x][0]]+cnt[x]+GetRank(ch[x][1],val);
    }
    int GetKth_pos;
    int GetKth(int x,int k)
    {
        if(x==0) return 0;
        if(siz[ch[x][0]]>=k) return GetKth(ch[x][0],k);
        if(siz[ch[x][0]]+cnt[x]>=k) return key[GetKth_pos=x];
        return GetKth(ch[x][1],k-siz[ch[x][0]]-cnt[x]);
    }
    /******************************** splay - ed ********************************/
    
    int main()
    {
        int n;
        cin>>n;
        Init();
        while(n--)
        {
            int opt,x;
            scanf("%d%d",&opt,&x);
            switch(opt)
            {
            case 1:
                Insert(x);
                break;
            case 2:
                Delete(x);
                break;
            case 3:
                printf("%d
    ",GetRank(root,x)-1);
                Splay(GetRank_pos,0);
                break;
            case 4:
                printf("%d
    ",GetKth(root,x+1));
                Splay(GetKth_pos,0);
                break;
            case 5:
                Insert(x);
                printf("%d
    ",key[GetMax(ch[root][0])]);
                Delete(x);
                break;
            case 6:
                Insert(x);
                printf("%d
    ",key[GetMin(ch[root][1])]);
                Delete(x);
                break;
            }
        }
    }
  • 相关阅读:
    如何用redis/memcache做Mysql缓存层?
    孤儿进程和僵尸进程总结
    二叉树的遍历(非递归)
    Linux进程分配内存的两种方式--brk() 和mmap()
    Hbase
    cgroup 分析之CPU和内存部分
    十道海量数据处理面试题与十个方法大总结
    快速定位性能瓶颈,检查出所有资源(CPU、内存、磁盘IO等)的利用率(utilization)、饱和度(saturation)和错误(error)度量,即USE方法
    红黑树
    tcp 两个重要窗口:滑动窗口 和 拥塞窗口
  • 原文地址:https://www.cnblogs.com/dilthey/p/9816218.html
Copyright © 2011-2022 走看看