1. 程式人生 > >Luogu 3369 / BZOJ 3224 - 普通平衡樹 - [無旋Treap]

Luogu 3369 / BZOJ 3224 - 普通平衡樹 - [無旋Treap]

題目連結:

https://www.lydsy.com/JudgeOnline/problem.php?id=3224

https://www.luogu.org/problemnew/show/P3369

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]

 

可能最近搞平衡二叉搜尋樹上癮了?搞完替罪羊樹再來搞搞無旋Treap。

關於無旋Treap:

我們已經知道了Treap是怎麼寫的:

BZOJ 3224 - 普通平衡樹 - [Treap][Splay]

我們知道,普通的Treap是要zigzag的,而無旋Treap顧名思義就是不需要zigzag。

無旋Treap最基本的(也是核心的)操作只有兩種,一種 $Merge$,一種 $Split$。

  $Split(x,k,a,b)$ 拆分操作:按照一個判定值 $k$ 將一個Treap $x$ 拆成兩個Treap $a,b$(左樹和右樹),滿足左樹的所有值均小於等於 $k$,右樹的所有值都大於 $k$。

  $Merge(x,a,b)$ 合併操作:將兩個Treap $a,b$ (滿足 $a$ 中所有元素均小於 $b$ 中所有元素),合併成一個Treap $x$。合併的原則是節點的堆權值滿足堆性質,所以是一種平衡樹。

另外,與普通Treap不同的是,每個節點均只存一個元素;也就是說,若存在若干個相同元素,會有若干個節點,而非一個節點用 $cnt$ 去記錄。

 

AC程式碼:

#include<bits/stdc++.h>
using namespace std;
const int INF=INT_MAX;
const int maxn=1e5+10;

/******************************** FHQ Treap - st ********************************/
int root,nodecnt;
int ch[maxn][2];
int key[maxn],dat[maxn];
int siz[maxn];
int NewNode(int val)
{
    int x=++nodecnt;
    key[x]=val, dat[x]=rand();
    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]]+1;
}
void Init()
{
    root=nodecnt=0;
    key[0]=dat[0]=0;
    siz[0]=0, ch[0][0]=ch[0][1]=0;
}
void Split(int x,int k,int &a,int &b)
{
    if(x==0)
    {
        a=b=0;
        return;
    }
    if(key[x]<=k) a=x, Split(ch[x][1],k,ch[a][1],b);
    else b=x, Split(ch[x][0],k,a,ch[b][0]);
    Pushup(x);
}
void Merge(int &x,int a,int b)
{
    if(a==0 || b==0)
    {
        x=a+b;
        return;
    }
    if(dat[a]<dat[b]) x=a, Merge(ch[x][1],ch[a][1],b);
    else x=b, Merge(ch[x][0],a,ch[b][0]);
    Pushup(x);
}

int GetRank(int val)
{
    int a=0,b=0;
    Split(root,val-1,a,b);
    int res=siz[a]+1;
    Merge(root,a,b);
    return res;
}
int GetKth(int x,int k)
{
    if(x==0) return INF;
    if(siz[ch[x][0]]+1==k) return key[x];
    if(siz[ch[x][0]]>=k) return GetKth(ch[x][0],k);
    else return GetKth(ch[x][1],k-siz[ch[x][0]]-1);
}
void Insert(int val)
{
    int a=0,b=0;
    Split(root,val,a,b);
    Merge(a,a,NewNode(val));
    Merge(root,a,b);
}
void Remove(int val)
{
    int a=0,b=0,c=0;
    Split(root,val,a,b);
    Split(a,val-1,a,c);
    Merge(c,ch[c][0],ch[c][1]);
    Merge(a,a,c);
    Merge(root,a,b);
}
int GetPre(int val)
{
    int a=0,b=0;
    Split(root,val-1,a,b);
    int res=GetKth(a,siz[a]);
    Merge(root,a,b);
    return res;
}
int GetNxt(int val)
{
    int a=0,b=0;
    Split(root,val,a,b);
    int res=GetKth(b,1);
    Merge(root,a,b);
    return res;
}
/******************************** FHQ Treap - ed ********************************/

int main()
{
    int n,opt,x;
    scanf("%d",&n);
    Init();
    while(n--)
    {
        scanf("%d%d",&opt,&x);
        if(opt==1) Insert(x);
        if(opt==2) Remove(x);
        if(opt==3) printf("%d\n",GetRank(x));
        if(opt==4) printf("%d\n",GetKth(root,x));
        if(opt==5) printf("%d\n",GetPre(x));
        if(opt==6) printf("%d\n",GetNxt(x));
    }
}