洛谷P3369 【模板】普通平衡树 treap

网址:https://www.luogu.org/problem/P3369

题意:

编写一个数据结构在每次$O(logn)(1 \leq n \leq 1e6)$完成以下功能:

一、插入一个数到序列中;二、在序列中删除某一个数;三、找到第$k$大;四、询问第$k$大的数;五、找到$x$的前驱,六、找到$x$的后继。

题解:

很显然,二叉搜索树就可以完成这个任务,不过最坏情况下,二叉树会退化成链,就会超时,因此我们就需要平衡二叉树,其中较为容易编写,速度又快的是treap。treap分为有旋treap和无旋treap,前者速度快,但不支持持久化,后者速度慢,但支持持久化。

一、有旋treap

treap的节点的权值维护了二叉树的性质,为了平衡,就需要通过另外一个数维护堆性质使其平衡,这个数就是每一个节点对应的随机数。旋转的时候按照随机数进行旋转,分为左旋和右旋,如图:

(参考博客:https://blog.csdn.net/K346K346/article/details/50808879

左旋和右旋

显然,右旋就是原树根的左子树变成树根,然后原树根的左子树的右子树变成原树根的左子树,左旋同理。易证旋转后二叉树性质不变。

插入时,先找到插入点,然后回溯时旋转。

删除较为复杂,删除时,先找到删除点,然后观察子树的随机数值选择一个子树作为删除后的树根,然后通过旋转把需要删除的节点移动到叶子后删除。

查询操作易于理解,看代码即可。

AC代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN=100005;
const int inf=0x3f3f3f3f;
struct Treap
{
    struct node
    {
        int val, rnd, lc, rc, size, num;
    };
    int cnt=0;
    node tr[MAXN];
    void init()
    {
        cnt=0;
    }
    int _rand()
    {
        static int seed=12345;
        return seed=(int)seed*482711LL%2147483647; 
    }
    void pushup(int p)
    {
        tr[p].size=tr[tr[p].lc].size+tr[tr[p].rc].size+tr[p].num;
    }
    void right(int &k)
    {
        int tmp=tr[k].lc;
        tr[k].lc=tr[tmp].rc;
        tr[tmp].rc=k;
        tr[tmp].size=tr[k].size;
        pushup(k);
        k=tmp;
    }
    void left(int &k)
    {
        int tmp=tr[k].rc;
        tr[k].rc=tr[tmp].lc;
        tr[tmp].lc=k;
        tr[tmp].size=tr[k].size;
        pushup(k);
        k=tmp;
    }
    void insert(int &p,int x)
    {
        if(p==0)
        {
            p=++cnt;
            tr[p].val=x;
            tr[p].num=tr[p].size=1;
            tr[p].lc=tr[p].rc=0;
            tr[p].rnd=_rand();
            return;
        }
        ++tr[p].size;
        if(x==tr[p].val)
            ++tr[p].num;
        else if(x<tr[p].val)
        {
            insert(tr[p].lc,x);
            if(tr[tr[p].lc].rnd<tr[p].rnd)
                right(p);
        }
        else if(x>tr[p].val)
        {
            insert(tr[p].rc,x);
            if(tr[tr[p].rc].rnd<tr[p].rnd)
                left(p);
        }
    }
    void del(int &p,int x)
    {
        if(p==0)
            return;
        if(tr[p].val==x)
        {
            if(tr[p].num>1)
                --tr[p].num,--tr[p].size;
            else
            {
                if(tr[p].lc==0||tr[p].rc==0)
                    p=tr[p].lc+tr[p].rc;
                else if(tr[tr[p].lc].rnd<tr[tr[p].rc].rnd)
                    right(p),del(p,x);
                else if(tr[tr[p].lc].rnd>tr[tr[p].rc].rnd)
                    left(p),del(p,x);
            }
        }
        else if(tr[p].val<x)
            --tr[p].size,del(tr[p].rc,x);
        else
            --tr[p].size,del(tr[p].lc,x);
    }
    int queryrnk(int &p,int x)
    {
        if(p==0)
            return 0;
        else if(tr[p].val==x)
            return tr[tr[p].lc].size+1;
        else if(tr[p].val<x)
            return tr[tr[p].lc].size+tr[p].num+queryrnk(tr[p].rc,x);
        else 
            return queryrnk(tr[p].lc,x);
    }
    int querynum(int &p,int rnk)
    {
        if(p==0)
            return 0;
        if(tr[tr[p].lc].size>=rnk)
            return querynum(tr[p].lc,rnk);
        rnk-=tr[tr[p].lc].size;
        if(rnk<=tr[p].num)
            return tr[p].val;
        rnk-=tr[p].num;  
        return querynum(tr[p].rc,rnk);
    }
    int queryfront(int &p,int x)
    {
        if(p==0)
            return -inf;
        if(tr[p].val<x)
            return max(tr[p].val,queryfront(tr[p].rc,x));
        else if(tr[p].val>=x)
            return queryfront(tr[p].lc,x);
    }
    int queryback(int &p,int x)
    {
        if(p==0)
            return inf;
        if(tr[p].val>x)
            return min(tr[p].val,queryback(tr[p].lc,x));
        else if(tr[p].val<=x)
            return queryback(tr[p].rc,x);
    }
};
int pos;
Treap tr;
int main()
{
    int n;
    scanf("%d",&n);
    int m,k;
    tr.init();
    for(int i=0;i<n;++i)
    {
        scanf("%d%d",&m,&k);
        if(m==1)
            tr.insert(pos,k);
        else if(m==2)
            tr.del(pos,k);
        else if(m==3)
            printf("%d\n",tr.queryrnk(pos,k));
        else if(m==4)
            printf("%d\n",tr.querynum(pos,k));
        else if(m==5)
            printf("%d\n",tr.queryfront(pos,k));
        else if(m==6)
            printf("%d\n",tr.queryback(pos,k));
    }
    return 0;
}

  

 

 

 

猜你喜欢

转载自www.cnblogs.com/Aya-Uchida/p/11361489.html