全网最详细的fhq treap (非旋treap)讲解

Posted ilverene

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了全网最详细的fhq treap (非旋treap)讲解相关的知识,希望对你有一定的参考价值。

思路

非旋treap的思想是范浩强引入的,所以也被称为fhq treap。

主要操作为分裂和合并。

注释都在代码里了,基本每一行都有。

代码

/*
By Nero Claudius Caeser Augustus Germanicus,

Emperor of the Roman Empire.
*/ 
#include <bits/stdc++.h>

using namespace std;

namespace StandardIO{

    template<typename T>void read(T &x){
        x=0;T f=1;char c=getchar();
        for(; c<'0'||c>'9'; c=getchar()) if(c=='-') f=-1;
        for(; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
        x*=f;
    }

    template<typename T>void write(T x){
        if(x<0) putchar('-'),x*=-1;
        if(x>=10) write(x/10);
        putchar(x%10+'0');
    }

} using namespace StandardIO;

namespace Project{
    #define int long long
    
    const int N=500005;
    
    int n,op,v; 
    int tot,root;// 节点总数和根节点。 
    int ch[N][2],size[N],rnd[N],val[N]; // ch表示两个儿子,size表示子树(不包括自己)的大小,rnd表示随机权值,val表示节点储存值。 
    
    inline int newNode(int _v){++tot,val[tot]=_v,size[tot]=1,rnd[tot]=rand();return tot;} // newNode返回一个新节点的位置。 
    inline void pushup(int pos){size[pos]=size[ch[pos][0]]+size[ch[pos][1]]+1;} // pushup更新节点的大小。 
    int merge(int x,int y){ // merge把两个treap合并。 (我们假定y比x大) 
        if(!x||!y) return x+y; // 如果有不是都存在,直接返回就好了。 
        if(rnd[x]<rnd[y]){ // 根据随机权值决定谁做父亲,以此保证平衡性。 
            ch[x][1]=merge(ch[x][1],y); // 由于y的随机权值比较大,所以让它做儿子,滚到右边去。 
            pushup(x); // 更新大小。 
            return x; // 返回位置。 
        }else{ // 反之同理。 
            ch[y][0]=merge(x,ch[y][0]);
            pushup(y);
            return y;
        }
    }
    void split(int now,int k,int &x,int &y){ // 把now树按照权值k分成两半,存入x和y里面。 当然也有按照size分裂的,但是此处不需要。 
        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]); // 反之。 
            pushup(now); // 更新大小。 
        }
    }
    int kth(int now,int k){ // 返回now树上的k小点。 
        while(1){
            if(size[ch[now][0]]>=k) now=ch[now][0]; // 由于左子树大小比k大,所以肯定在左子树中。 
            else{
                if(k==size[ch[now][0]]+1) return now; // 找到答案了,返回。 
                k-=size[ch[now][0]]+1,now=ch[now][1]; // 在右子树中。 
            }
        }
    }

    void MAIN(){
        srand((unsigned)time(NULL));
        read(n);
        while(n--){
            read(op),read(v);
            if(op==1){ // 插入操作。 
                int x,y;
                split(root,v,x,y); // 把树按照v分裂。 
                root=merge(merge(x,newNode(v)),y); // 在原来的两半中间插入v,然后merge在一起,比较显然。 
            }else if(op==2){ // 删除操作。 
                int x,y,z;
                split(root,v,x,z); // v其实在以v分裂的左半边的右边。 
                split(x,v-1,x,y);
                y=merge(ch[y][0],ch[y][1]); // 把v给丢掉。 
                root=merge(merge(x,y),z);
            }else if(op==3){ // 查询排名。 
                int x,y;
                split(root,v-1,x,y); // 按v分裂,这样可以得到比自己小的数的个数。 
                write(size[x]+1),putchar('
');
                root=merge(x,y);
            }else if(op==4){ // 查询排名为x的数。 
                write(val[kth(root,v)]),putchar('
'); // 略。。 
            }else if(op==5){ // 求前驱。 
                int x,y;
                split(root,v-1,x,y); // 原理参考删除操作。 
                write(val[kth(x,size[x])]),putchar('
');
                root=merge(x,y);
            }else{ // 求后继。 
                int x,y;
                split(root,v,x,y); // 略 
                write(val[kth(y,1)]),putchar('
');
                root=merge(x,y);
            }
        }
    }
    
    #undef int
}

int main(){
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    Project::MAIN();
}
 

以上是关于全网最详细的fhq treap (非旋treap)讲解的主要内容,如果未能解决你的问题,请参考以下文章

非旋 treap 结构体数组版(无指针)详解,有图有真相

非旋Treap

非旋Treap总结 : 快过Splay 好用过传统Treap

非旋Treap

fhq treap

非旋treap