[模板]普通平衡树

3224: Tyvj 1728 普通平衡树

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 24082  Solved: 11089
[Submit][Status][Discuss]

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$

二叉搜索树+堆,将权值形成二叉搜索树,并且在随机$rand$第二关键字从而将其转成堆,并且在此时不破坏二叉搜索树。因为堆并且随机因素所以期望时间复杂度$O(nlog n)$

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<climits>
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int N=300001;
struct node{
    int l,r,cnt,num,size,rnk;
}tr[N];
int n,root,tot;
void update(int k){
    tr[k].size=tr[k].cnt;
    tr[k].size+=tr[tr[k].l].size;
    tr[k].size+=tr[tr[k].r].size;return;
}
void zag(int &k){
    int tp=tr[k].l;
    tr[k].l=tr[tp].r;
    tr[tp].r=k;
    tr[tp].size=tr[k].size;
    update(k);
    k=tp;
    return;
}
void zig(int &k){
    int tp=tr[k].r;
    tr[k].r=tr[tp].l;
    tr[tp].l=k;
    tr[tp].size=tr[k].size;
    update(k);
    k=tp;
    return;
}
void insert(int x,int &k){
    if(k==0){
        k=++tot;
        tr[k].cnt=tr[k].size=1;tr[k].num=x;
        tr[k].rnk=rand();
        return;
    }
    tr[k].size++;
    if(x==tr[k].num){tr[k].cnt++;return;}
    if(x<tr[k].num){
        insert(x,tr[k].l);
        if(tr[tr[k].l].rnk<tr[k].rnk)zag(k);
    }else{
        insert(x,tr[k].r);
        if(tr[tr[k].r].rnk<tr[k].rnk) zig(k);
    }
    return;
}
void del(int x,int &k){
    if(x==tr[k].num){
        if(tr[k].cnt>1){tr[k].size--,tr[k].cnt--;return;}
        if(tr[k].l*tr[k].r==0) {k=tr[k].l+tr[k].r;return;}
        if(tr[tr[k].l].rnk<tr[tr[k].r].rnk){zag(k);del(x,k);return;}
        else{zig(k);del(x,k);return;}
    }
    tr[k].size--;
    if(x<tr[k].num) del(x,tr[k].l);
    else del(x,tr[k].r);
    return;
}
int rank_x(int x,int k){
    if(x==tr[k].num) return tr[tr[k].l].size+1;
    if(x<tr[k].num) return rank_x(x,tr[k].l);
    return tr[tr[k].l].size+tr[k].cnt+rank_x(x,tr[k].r);
}
int rank(int x,int k){
    if(k==0) return 0;
    if(tr[tr[k].l].size<x&&tr[tr[k].l].size+tr[k].cnt>=x) return tr[k].num;
    if(x<=tr[tr[k].l].size) return rank(x,tr[k].l);
    return rank(x-tr[tr[k].l].size-tr[k].cnt,tr[k].r);
}
int pre(int x,int k){
    if(k==0) return INT_MIN;
    if(x<=tr[k].num) return pre(x,tr[k].l);
    return max(tr[k].num,pre(x,tr[k].r));
}
int nex(int x,int k){
    if(k==0) return INT_MAX;
    if(x>=tr[k].num) return nex(x,tr[k].r);
    return min(tr[k].num,nex(x,tr[k].l));
} 
int main(){
    srand(time(0));
    n=read();
    while(n--){
        int opt=read(),x=read();
        if(opt==1) insert(x,root);
        if(opt==2) del(x,root);
        if(opt==3) printf("%d
",rank_x(x,root));
        if(opt==4) printf("%d
",rank(x,root));
        if(opt==5) printf("%d
",pre(x,root));
        if(opt==6) printf("%d
",nex(x,root));
    }
    return 0;
}
View Code

$Splay$

$Splay$与$Treap$都是在二叉搜索树上维护树高,而$Treap$用第二关键字维护,而$Splay$巧妙的运用双旋维护,均摊时间复杂度是$O(n log  n)$,证明请读者自己证明。

原文地址:https://www.cnblogs.com/si-rui-yang/p/10190089.html