1. 程式人生 > >非旋轉Treap詳解

非旋轉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\n",val[kth(x,size[x])]);
root=merge(x,y);
//前驅

split(root,a,x,y);
printf("%d\n",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\n",size[x]+1);
            root=merge(x,y);
        }
        if(opt==4)
            printf("%d\n",val[kth(root,a)]);
        if(opt==5)
        {
            split(root,a-1,x,y); 
            printf("%d\n",val[kth(x,size[x])]);
            root=merge(x,y);
        }
        if(opt==6)
        {
            split(root,a,x,y);
            printf("%d\n",val[kth(y,1)]);
            root=merge(x,y);
        }
    }
    return 0;
}