treap

treap

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:插入x数,删除x数(若有多个相同的数,因只删除一个),查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名),查询排名为x的数,求x的前驱(前驱定义为小于x,且最大的数),求x的后继(后继定义为大于x,且最小的数)。

treap似乎就是一个保持堆性质的二叉搜索树。结点的优先级是随机分配的。可以保证treap的所有操作,期望时间复杂度都为logn。

代码中大量运用了递归操作。注意一个坑点是删除操作的最后需要update一下。

#include <ctime>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;

const int maxn=1e5+5, INF=1e9;
int randint(){  //比手写rand效果好
    return rand();
}

void get(int &x){
    static int flag; static char c; flag=1;  //static的变量只初始化一次
    for (c=getchar(); !isdigit(c); c=getchar())
        if (c=='-') flag=-1;
    for (x=c-48; c=getchar(), isdigit(c); )
        x=(x<<3)+(x<<1)+c-48; x*=flag;
}

struct Treap{
    //时刻牢记旋转操作不破坏bst的性质
    //小根堆  x:结点编号
    //空间复杂度:插入次数
    //v:结点的值  w:结点的优先级
    int tot, root, siz[maxn], v[maxn], w[maxn], s[maxn][2];
    void up(int x){ siz[x]=siz[s[x][0]]+siz[s[x][1]]+1; }
    void spin(int &x, int p){  //把x所指的p孩子旋转上来
        int t=s[x][p];
        s[x][p]=s[t][!p]; s[t][!p]=x;  //重构关系
        up(x); up(t); x=t;  //维护子树大小  将当前位置更换
    }
    void ins(int &x, int c){  //不停把优先级小的转上来
        if (!x){ x=++tot; siz[x]=1;
            v[x]=c; w[x]=randint(); return; }
        ++siz[x];
        if (c<=v[x]){ ins(s[x][0], c);  //左边是v小于等于v[x]的结点
            if (w[s[x][0]]<w[x]) spin(x, 0); }
        else { ins(s[x][1], c);
            if (w[s[x][1]]<w[x]) spin(x, 1); }
    }
    void del(int &x, int c){  //把这个数一直转到叶子节点然后删除
        if (v[x]==c){
            if (!s[x][0]||!s[x][1]){
                x=s[x][0]+s[x][1]; return; }
            if (w[s[x][0]]>w[s[x][1]]){
                spin(x, 1); del(s[x][0], c); }
            else{ spin(x, 0); del(s[x][1], c); }
        }  //别忘记这里也要update!
        else if (v[x]>c) del(s[x][0], c);
        else del(s[x][1], c);
        up(x);  //由于没有旋转操作需要手动维护
    }
    int rank(int x, int c){  //询问c的最小排名
        if (!x) return 1;
        if (c<=v[x]) return rank(s[x][0], c);
        else return rank(s[x][1], c)+siz[s[x][0]]+1;
    }
    int atrank(int x, int c){  //询问第c个数
        if (siz[s[x][0]]==c-1) return v[x];
        if (siz[s[x][0]]>=c) return atrank(s[x][0], c);
        else return atrank(s[x][1], c-siz[s[x][0]]-1);
    }
    int pre(int x, int c){  //询问小于c且最大的数
        if (!x) return -INF;
        if (v[x]<c) return max(v[x], pre(s[x][1], c));  //答案有可能是根
        else return pre(s[x][0], c);
    }
    int nxt(int x, int c){  //询问大于c且最小的数
        if (!x) return INF;
        if (v[x]>c) return min(v[x], nxt(s[x][0], c));  //答案有可能是根
        else return nxt(s[x][1], c);
    }
}treap;

int n, op, x;

int main(){
    get(n); srand(time(NULL));
    for (int i=0; i<n; ++i){
        get(op); get(x);
        if (op==1) treap.ins(treap.root, x);
        if (op==2) treap.del(treap.root, x);
        if (op==3) printf("%d\n", treap.rank(treap.root, x));
        if (op==4) printf("%d\n", treap.atrank(treap.root, x));
        if (op==5) printf("%d\n", treap.pre(treap.root, x));
        if (op==6) printf("%d\n", treap.nxt(treap.root, x));
    }
    return 0;
}
posted @ 2018-03-22 09:05  pechpo  阅读(350)  评论(0编辑  收藏  举报