fhq_treap

数据结构 fhq_treap

各种阴差阳错地得知了这个数据结构,于是学习了一波

fhq_treap,非旋treap,据说是一位叫范浩强的人发明的。该treap厉害在虽然思路类似于treap,但无需旋转操作。可以在经可能维持空间形态(方便可持久化改造)的同时保证时空复杂度

fhq_treap的各种操作总结下来归功于两个基本操作:分裂(split)和合并(merge)

分裂分为两类,权值分裂和大小分裂。注意:两者虽然写法极其类似,但各自有不同的用途

分裂

权值分裂:(平衡树的话用这个!)

void split(int now,int k,int &x,int &y){
        if(!now) x=y=0;
        else{
            if(val[now]<=k) {x=now; split(ch[now][1],k,ch[now][1],y);}
            else {y=now; split(ch[now][0],k,x,ch[now][0]);}
            updata(now);
        }
    }

大小分裂:

void split(int now,int k,int &x,int &y){
        if(!now) x=y=0;
        else{
            int t=size[ch[now][0]]+1;
            if(t<=k) {x=now; split(ch[now][1],k-t,ch[now][1],y);}
            else {y=now; split(ch[now][0],k,x,ch[now][0]);}
            updata(now);
        }
    }

初看的时候不好理解,画图看看思索就好了

先忽略到两个难以理解的引用,先看now和k:这无非就是now在不断挑来挑去对吧,保证now在满足k的同时,经可能出现在边界(大概是这个意思)

再尝试理解一下引用:

Z31eTP.png

个人感觉把自己的见解比较形象地提炼出来了

合并

int merge(int a,int b){
        if(!a||!b) return a+b;
        if(rad[a]<rad[b]) {ch[a][1]=merge(ch[a][1],b); updata(a); return a;}
        else {ch[b][0]=merge(a,ch[b][0]); updata(b); return b;}
    }

合并没什么好说的。注意ch[a][0]和ch[b][1]都已经排好序了,故无需再次合并

剩下地就是用这两个操作去实现各种功能:

插入:

void insert(int k){
        split(root,k,x,y);
        root=merge(merge(x,newnode(k)),y);
    }

删除:

void delate(int k){
        split(root,k,x,z);
        split(x,k-1,x,y);
        y=merge(ch[y][0],ch[y][1]);
        root=merge(merge(x,y),z);
    }

查询x的排名:

int rnk(int k){
        split(root,k-1,x,y);
        int ans=size[x]+1;
        root=merge(x,y);
        return ans;
    }

查询排名为x的数字:

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

查询x的前驱:

int pre(int k){
        split(root,k-1,x,y);
        int ans=val[kth(x,size[x])];
        root=merge(x,y);
        return ans;
    }

查询x的后驱:

int suf(int k){
        split(root,k,x,y);
        int ans=val[kth(y,1)];
        root=merge(x,y);
        return ans;
    }

运用好分裂和合并,平衡树也可以告别难受的旋转ヾ(≧▽≦*)o

练习:【模板】普通平衡树

完整代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define FT faQ_treap::

inline int read();

namespace faQ_treap{
    const int MAX=1e5+5;
    int tot,root;
    int val[MAX],ch[MAX][2],size[MAX],rad[MAX],x,y,z;

    void updata(int k){
        size[k]=size[ch[k][0]]+size[ch[k][1]]+1;
    }

    int newnode(int k){
        ++tot;
        val[tot]=k;
        size[tot]=1;
        rad[tot]=rand();
        return tot;
    }

    void split(int now,int k,int &x,int &y){
        if(!now) x=y=0;
        else{
            if(val[now]<=k) {x=now; split(ch[now][1],k,ch[now][1],y);}
            else {y=now; split(ch[now][0],k,x,ch[now][0]);}
            updata(now);
        }
    }

    int merge(int a,int b){
        if(!a||!b) return a+b;
        if(rad[a]<rad[b]) {ch[a][1]=merge(ch[a][1],b); updata(a); return a;}
        else {ch[b][0]=merge(a,ch[b][0]); updata(b); return b;}
    }

    void insert(int k){
        split(root,k,x,y);
        root=merge(merge(x,newnode(k)),y);
    }

    void delate(int k){
        split(root,k,x,z);
        split(x,k-1,x,y);
        y=merge(ch[y][0],ch[y][1]);
        root=merge(merge(x,y),z);
    }

    int rnk(int k){
        split(root,k-1,x,y);
        int ans=size[x]+1;
        root=merge(x,y);
        return ans;
    }

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

    int pre(int k){
        split(root,k-1,x,y);
        int ans=val[kth(x,size[x])];
        root=merge(x,y);
        return ans;
    }

    int suf(int k){
        split(root,k,x,y);
        int ans=val[kth(y,1)];
        root=merge(x,y);
        return ans;
    }

    bool check(int k){
        int u=root;
        while(u){
            if(val[u]==k) return true;
            if(val[u]>k) u=ch[u][0];
            else u=ch[u][1];
        }
        return false;
    }
}

int n,type,k;

int main(){
    #ifndef ONLINE_JUDGE
    freopen("test.in","r",stdin);
    #endif

    n=read();
    for(int i=1;i<=n;++i){
        type=read(); k=read();
        switch(type){
            case 1:
            FT insert(k);
            break;

            case 2:
            FT delate(k);
            break;

            case 3:
            printf("%d\n",FT rnk(k));
            break;

            case 4:
            printf("%d\n",FT val[FT kth(FT root,k)]);
            break;

            case 5:
            printf("%d\n",FT pre(k));
            break;

            case 6:
            printf("%d\n",FT suf(k));
            break;
        }
    }

    return 0;
}

inline int read(){
    char tmp=getchar(); int sum=0; bool flag=false;
    while(tmp<'0'||tmp>'9'){
        if(tmp=='-') flag=true;
        tmp=getchar();
    }
    while(tmp>='0'&&tmp<='9'){
        sum=(sum<<1)+(sum<<3)+tmp-'0';
        tmp=getchar();
    }
    return flag?-sum:sum;
}

fhq_treap的权值分裂应用于平衡树的实现,可以完成平衡树可以完成的全部内容。

fhq_的大小分裂主要应用于解决区间问题。具体请参考我的博客上"题解 [NOI2005]维护数列"

posted @ 2020-06-29 21:52  ticmis  阅读(169)  评论(0)    收藏  举报