非旋转Treap详解

Posted 342zhuyongqi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了非旋转Treap详解相关的知识,希望对你有一定的参考价值。

利用其他人其中考试的时间,终于学完了非旋转Treap,它与普通Treap的区别就是它不旋转废话。前置知识只有BST和可并堆。

BST看这个博客,解释的挺清楚的。https://www.cnblogs.com/jiangminghong/p/9999884.html

可并堆就是用很快的时间合并两个堆。如果裸上一个并查集的话就是nlog2n.这个复杂度我们是不能接受的。正常的可并堆是维护一棵左偏树,我们用一个参数dis[x]表示从x点出发能够向右走的最大步数。每次两个堆合并时,我们就把一个堆扔到另一个堆里就行了。

在旋转Treap中,我们用随机数来保持平衡树的平衡.非旋转Treap也是同样的做法。

1 int lson[N];//表示当前点的左儿子
2 int rson[N];//表示当前点的右儿子
3 int key[N];//表示当前点的随机数(保持平衡树的平衡)
4 int val[N];//表示当前点的权值
5 int size[N];//表示当前点子树的大小

这就是我们需要维护的信息。

看看操作吧

 1.Merge【合并】O(logn)
 2.Split【拆分】O(logn)

只有两个

可资瓷使用范围

1.Insert Newnode+Merge O(logn)

2.Delete Split+Split+Merge O(logn)

3.Find_kth Split+Split O(logn)

4.Query  Split+Split  O(logn)

5.各种区间操作

1.split

split操作就是以一个点为界限,将平衡树分裂成两棵平衡树,保证左边平衡树中任意点权值小于右边平衡树中任意点的权值。

以将平衡树分裂成权值<=val和权值>val两部分为例:从根节点开始往下查找,当当前点权值<=val时,将当前点及它的右子树接到分裂后第二棵平衡树的左子树上;反之则将当前点及它的左子树接到分裂后第一棵平衡树的右子树上,直到找到叶子节点为止。

void split(int now,int k,int &x,int &y)//now表示现在的位置,k表示要查询的权值,x,y分别为拆分后两子树的根
{
    if(!now)
    {
        x=y=0;
        return ;
    }
    if(val[now]<=k)
        x=now,split(rson[now],k,rson[now],y);
    else
        y=now,split(lson[now],k,x,lson[now]);
    push_up(now);
    return ;
}

如果想要拆分成前K个点与后n-K个点也是同理的,判断的条件就是size喽!

void split(int now,int k,int &x,int &y)
{
    if(!now)
    {
        x=y=0;
        return ;
    }
    if(size[lson[now]]<k)
        x=now,split(rson[now],k-size[lson[now]]-1,rson[now],y);
    else
        y=now,split(lson[now],k,x,lson[now]);
    push_up(now);
}

2.merge

 merge这个操作就是将两个堆合并,我们请出可并堆。可并堆的合并方式就是按照随机数大小来合并。

int merge(int x,int y)
{
    if(!x||!y)
        return x|y;
    if(key[x]<key[y])
    {
        rson[x]=merge(rson[x],y);
        push_up(x);
        return x;
    }
    else
    {
        lson[y]=merge(x,lson[y]);
        push_up(y)
        return y;
    }
}

有木有一直好奇push_up函数啊,其实和线段树一样,维护子树的一些信息。

有了这两个sao操作,是不是就可以在序列上想干嘛就干嘛了?

1.Newnode

新建节点,加入平衡树

int newnode(int x)
{
    size[++tot]=1;
    val[tot]=x;
    key[tot]=rand()*rand();
    return tot;
}


split(root,a,x,y);
root=merge(merge(x,newnode(a)),y);

2.Delete

划分成3个区间,之后合并

split(root,a,x,z);
split(x,a-1,x,y);
y=merge(lson[y],rson[y]);
root=merge(merge(x,y),z);

3.Query(查询以x为根排名第K的数)

判断K和左子树大小的关系,根据size找到答案。

int kth(int now,int k)
{
    while(1)
    {
        if(now==0)
            break;
        if(k<=size[lson[now]])
            now=lson[now];
        else if(k==size[lson[now]])
            return now;
        else
            k-=size[lson[now]]+1,now=rson[now];
    }
    return 0;
}

4.前驱&后继

split(root,a-1,x,y); 
printf("%d
",val[kth(x,size[x])]);
root=merge(x,y);
//前驱

split(root,a,x,y);
printf("%d
",val[kth(y,1)]);
root=merge(x,y);
//后继

LuoguP3369 普通平衡树

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 1000100
int tot;
int size[N];
int lson[N];
int rson[N];
int key[N];
int val[N];
int n,m;
int root;
int x,y;
void push_up(int x){size[x]=size[lson[x]]+size[rson[x]]+1;return ;}
int newnode(int x)
{
    size[++tot]=1;
    val[tot]=x;
    key[tot]=rand();
    return tot;
}
int merge(int x,int y)
{
    if(!x||!y)
        return x+y;
    if(key[x]<key[y])
    {
        rson[x]=merge(rson[x],y);
        push_up(x);
        return x;
    }
    else
    {
        lson[y]=merge(x,lson[y]);
        push_up(y);
        return y;
    }
}
void split(int now,int k,int &x,int &y)
{
    if(!now)
        x=y=0;
    else
    {
        if(val[now]<=k)
            x=now,split(rson[now],k,rson[now],y);
        else
            y=now,split(lson[now],k,x,lson[now]);
        push_up(now);
    }
    return ;
}
int kth(int now,int k)
{
    while(1)
    {
        if(k<=size[lson[now]])
            now=lson[now];
        else if(k==size[lson[now]]+1)
            return now;
        else
            k-=size[lson[now]]+1,now=rson[now];
    }
    return 0;
}
int main()
{
    srand(20030305);
    scanf("%d",&n);
    int opt,a;
    while(n--)
    {
        scanf("%d%d",&opt,&a);
        if(opt==1)
        {
            split(root,a,x,y);
            root=merge(merge(x,newnode(a)),y);
        }
        if(opt==2)
        {
            int z;
            split(root,a,x,z);
            split(x,a-1,x,y);
            y=merge(lson[y],rson[y]);
            root=merge(merge(x,y),z);
        }
        if(opt==3)
        {
            split(root,a-1,x,y);
            printf("%d
",size[x]+1);
            root=merge(x,y);
        }
        if(opt==4)
            printf("%d
",val[kth(root,a)]);
        if(opt==5)
        {
            split(root,a-1,x,y); 
            printf("%d
",val[kth(x,size[x])]);
            root=merge(x,y);
        }
        if(opt==6)
        {
            split(root,a,x,y);
            printf("%d
",val[kth(y,1)]);
            root=merge(x,y);
        }
    }
    return 0;
}

 

以上是关于非旋转Treap详解的主要内容,如果未能解决你的问题,请参考以下文章

[bzoj1861][Zjoi2006]Book 书架_非旋转Treap

可持久化Treap(fhq Treap,非旋转式Treap)学习(未完待续)

模板非旋转Treap

[bzoj1895][Pku3580]supermemo_非旋转Treap

[bzoj3173]最长上升子序列_非旋转Treap

可持久化平衡树详解及实现方法分析