zoukankan      html  css  js  c++  java
  • [总结] fhq_Treap 学习笔记

    转自

    无旋版 $Treap$。

    只需要两个操作即可达到 $splay$ 的所有功能

    1、$split$

    它的主要思想就是把一个 $Treap$ 分成两个。

     $split$ 操作有两种类型,一种是按照权值分配,一种是按前 k 个分配。

    第一种就是把所有小于 k 的权值的节点分到一棵树中,第二种是把前 k 个分到一个树里。

    权值版:

     1 void split(int o,int k,int &x,int &y){ //这里的x,y分别是将以o为根的树切开后第一个新子树的根和第二个新子树的根
     2     if(!o) x=y=0;
     3     else {
     4         if(val[o]<=k)
     5             x=o,split(ch[o][1],k,ch[o][1],y);
     6         else 
     7             y=o,split(ch[o][0],k,x,ch[o][0]);
     8         pushup(o);
     9     }
    10 }

    对于我们遍历到每一个点,假如它的权值小于k,那么它的所有左子树,都要分到左边的树里,然后遍历它的右儿子。假如大于k,把它的所有右子树分到右边的树里,遍历左儿子。

    因为它的最多操作次数就是一直分到底,效率就是 $O(logn)$。

    对于前k个版的,就是像找第k大的感觉。每次减掉sze

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

    2、$merge$

    这个就是把两个 $Treap$ 合成一个,保证第一个的权值小于第二个。

    因为第一个 $Treap$ 的权值都比较小,我们比较一下它的 $prio$ (优先级),假如第一个的 $prio$ 小,我们就可以直接保留它的所有左子树,接着把第一个 $Treap$ 变成它的右儿子。反之,我们可以保留第二棵的所有右子树,指针指向左儿子。

    你可以把这个过程形象的理解为在第一个 $ Treap$ 的右子树上插入第二个树,也可以理解为在第二个树的左子树上插入第一棵树。因为第一棵树都满足小于第二个树,所以就变成了比较 $prio$ 来确定树的形态。

    也就是说,我们其实是遍历了第一个$Treap$ 的根->最大节点,第二个$Treap$的根->最小节点,也就是 $O(logn)$

    int merge(int x,int y){
        if(!x or !y) return x+y;
        if(prio[x]<prio[y]){
            ch[x][1]=merge(ch[x][1],y);
            pushup(x);
            return x;
        }
        else{
            ch[y][0]=merge(x,ch[y][0]);
            pushup(y);
            return y;
        }
    }

    下面我们就可以通过这两个基本的东西实现各种各样的操作了。

    3、insert

    插入一个权值为 $k$ 的点,把树按照 $k$ 的权值 $split$ 成两个,再 $merge$ 回去。

    4、remove

    删除权值为 $k$ 的点,把树按照 $k$ 分成两个$a,b$ 再把$a$ 按照 $k-1$ 分成$c,d$。把$d$ 的两个儿子 $merge$起来,再 $merge(merge(c,d),b)$

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

    其它见代码

    // 普通平衡树 fhq_Treap
    // By YoungNeal
    #include<cstdio>
    #include<cstdlib>
    #define N 100005
    #define inf 0x3f3f3f3f
    
    int Root;
    int n,opt,x,tot;
    int val[N],prio[N];
    int sze[N],ch[N][2];
    
    void pushup(int o){
        sze[o]=sze[ch[o][0]]+sze[ch[o][1]]+1;
    }
    
    void split(int o,int k,int &x,int &y){
        if(!o) x=y=0;
        else {
            if(val[o]<=k)
                x=o,split(ch[o][1],k,ch[o][1],y);
            else 
                y=o,split(ch[o][0],k,x,ch[o][0]);
            pushup(o);
        }
    }
    
    int merge(int x,int y){
        if(!x or !y) return x+y;
        if(prio[x]<prio[y]){
            ch[x][1]=merge(ch[x][1],y);
            pushup(x);
            return x;
        }
        else{
            ch[y][0]=merge(x,ch[y][0]);
            pushup(y);
            return y;
        }
    }
    
    int newnode(int v){
        sze[++tot]=1;
        val[tot]=v;
        prio[tot]=rand();
        return tot;
    }
    
    void insert(int k){
        int x,y;
        split(Root,k,x,y);
        Root=merge(merge(x,newnode(k)),y);
    }
    
    void remove(int k){
        int x,y,z;
        split(Root,k,x,y);
        split(x,k-1,x,z);
        z=merge(ch[z][0],ch[z][1]);
        Root=merge(x,merge(z,y));
    }
    
    void kthrank(int k){
        int x,y;
        split(Root,k-1,x,y);
        printf("%d
    ",sze[x]+1);
        Root=merge(x,y);
    }
    
    int rank(int o,int k){
        if(sze[ch[o][0]]==k-1) return val[o];
        if(sze[ch[o][0]]>=k) return rank(ch[o][0],k);
        return rank(ch[o][1],k-sze[ch[o][0]]-1);
    }
    
    void prev(int k){
        int x,y;
        split(Root,k-1,x,y);
        printf("%d
    ",rank(x,sze[x]));
        Root=merge(x,y);
    }
    
    void nxt(int k){
        int x,y;
        split(Root,k,x,y);
        printf("%d
    ",rank(y,1));
        Root=merge(x,y);
    }
    
    signed main(){
        scanf("%d",&n);
        while(n--){
            scanf("%d%d",&opt,&x);
            if(opt==1) insert(x);
            if(opt==2) remove(x);
            if(opt==3) kthrank(x);
            if(opt==4) printf("%d
    ",rank(Root,x));
            if(opt==5) prev(x);
            if(opt==6) nxt(x);
        }
        return 0;
    }

    5、区间操作

    对于翻转区间 $[l,r]$,我们可以先把区间 $[1,l-1]$ $split$ 出来,再把 $[l,r]$ $split$ 出来就行了。注意 $lazy$ 标记及时清除。

    // 文艺平衡树 fhp_Treap
    // By YoungNeal
    #include<ctime>
    #include<cstdio>
    #include<cstdlib>
    #define N 100005
    
    int Root;
    int lazy[N];
    int n,m,cnt;
    int val[N],sze[N];
    int ch[N][2],prio[N];
    
    void pushup(int o){
        sze[o]=sze[ch[o][0]]+sze[ch[o][1]]+1;
    }
    
    void pushdown(int o){
        if(!lazy[o] or !o) return;
        ch[o][0]^=ch[o][1]^=ch[o][0]^=ch[o][1];
        lazy[ch[o][0]]^=1;
        lazy[ch[o][1]]^=1;
        lazy[o]=0;
    }
    
    void split(int o,int k,int &x,int &y){
        if(!o) x=y=0;
        else{
            pushdown(o);
            if(k>sze[ch[o][0]]) x=o,split(ch[o][1],k-sze[ch[o][0]]-1,ch[o][1],y);
            else y=o,split(ch[o][0],k,x,ch[o][0]);
            pushup(o);
        }
    }
    
    int merge(int x,int y){
        if(!x or !y) return x+y;
        pushdown(x); pushdown(y);
        if(prio[x]<prio[y]){
            ch[x][1]=merge(ch[x][1],y);
            pushup(x);
            return x;
        }
        else{
            ch[y][0]=merge(x,ch[y][0]);
            pushup(y);
            return y;
        }
    }
    
    int newnode(int v){
        val[++cnt]=v;
        sze[cnt]=1;
        prio[cnt]=rand();
        return cnt;
    }
    
    void res(int l,int r){
        int a,b,c,d;
        split(Root,r,a,b);
        split(a,l-1,c,d);
        lazy[d]^=1;
        Root=merge(merge(c,d),b);
    }
    
    void dfs(int now){
        if(!now) return;
        pushdown(now);
        dfs(ch[now][0]);
        printf("%d ",val[now]);
        dfs(ch[now][1]);
    }
    
    signed main(){
        srand(time(0));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            Root=merge(Root,newnode(i));
        //printf("Root=%d
    ",Root);
        for(int x,y,i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            res(x,y);
            //printf("i=%d
    ",i);
            //dfs(Root);
        }
        //printf("Root=%d
    ",Root);
        dfs(Root);
        return 0;
    }
  • 相关阅读:
    浏览器中Javascript单线程分析
    AJAX原理及XMLHttpRequest对象分析
    for和foreach的执行效率的问题之新发现
    C# 2.0泛型初试
    Page常用方法
    自动提交问题,不知道这算不算是MS的一个BUG
    什么是爱情【转载】
    IE 7 BETA 3安装
    Ajax实现两级下拉框连动的应用
    IFame的另一种表现
  • 原文地址:https://www.cnblogs.com/YoungNeal/p/8977328.html
Copyright © 2011-2022 走看看