zoukankan      html  css  js  c++  java
  • fhq-treap小结

    终于AC fhq-Treap啦!!!撒花庆祝qwq

    回头看看fhq-treap真的吼哇!

    推一波大佬的博客:https://www.cnblogs.com/ppprseter/p/9382132.html

    就是看这个学会的qwq

    进入正题:

    fhq-treap,也具有Treap的性质,即堆+bst的性质,在此基础上,它不需要进行旋转以维护平衡,而是用拆树和合并的操作让树保持平衡

    支持区间、效率高、可持久化!

    规定如下:

      val:按平衡树的权值

      key:按堆的权值

      sz:子树大小

      root:当前根

      ch[now][0] : 左儿子,ch[now][1]右儿子

      #define ls ch[now][0]    rs ch[now][1]

    操作1:split(拆树)

      将一个树拆成两个:

    void split(int now,int k,int &x,int &y)//x <= k,y > k
    {
        if(!now) 
        {        
            x = y = 0;
            return;
        }
        if(val[now] <= k) x = now,split(rs,k,rs,y);
        else y = now,split(ls,k,x,ls);
        update(now);
    }

        函数意义为:以now为根,将树拆成两部分,x部的权值全部<=k,y部>k    

        考虑当前节点now:如果val[now]<=k,那么now的左子树应当被分配给x(十分显然qwq),而now的右子树不确定,同时x的右子树也不确定,而y未被分配,因此进入下一层求解。>k同理

    操作2:merge(合并)

      将两个树合并,返回新树的根:

    int merge(int x,int y)//val:x <= y 
    {
        if(!x || !y) return x + y;
        if(key[x] < key[y])
        {
            ch[x][1] = merge(ch[x][1],y);
            update(x);
            return x;
        }
        else 
        {
            ch[y][0] = merge(x,ch[y][0]);
            update(y);
            return y;
        }
    }

         规定:val[x] < val[y]  

         如果x和y中有一个为空,那么返回另一个即可

         由于x,y是由split得来,因此二者均是bst,因此在合并时,只用考虑堆性质即可

    有了这两个,其他的操作就围绕他们来:

      新建点:不多说

    int New(int k)
    {
        key[++tot] = rand(),val[tot] = k,sz[tot] = 1;
        return tot;
    }

      插入:  

        先将树按插入数大小k分成两份,把k加到x中,再把树合并回去:

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

      删除:

        先把树按要删的数k分成x,y,此时x中所有数<=k,再把x按k-1分成x和z,此时z中所有数都等于k,删去z的根,就把z的左右儿子合并(相当于抛弃了z的根节点),然后再合并回去即可

    void del(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));
    }

       查k的排名:

        把树按k - 1分成两份(注意!不能按k分,由于可能有重,按k分无法得到正解),此时x部<=k - 1,y部>=k,输出x部大小即可

    void Rank(int k)
    {
        int x,y;
        split(root,k - 1,x,y);
        printf("%d
    ",sz[x] + 1);
        root = merge(x,y);
    }

        找第k大:

          与Treap相同

    void fRank(int now,int k)
    {
        while(1)
        {
            if(k <= sz[ls]) now = ls;
            else if(k > sz[ls] + 1) k -= sz[ls] + 1,now = rs;
            else { printf("%d
    ",val[now]); return;}
        }
    }

         前后继:

           由拆分和合并易得

    传送门:https://www.luogu.org/problemnew/show/P3369

    总体代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<climits>
    #include<vector>
    #include<cstdlib>
    #include<ctime>
    using namespace std;
    #define ls ch[now][0]
    #define rs ch[now][1]
    inline int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '-') op = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            (ans *= 10) += ch - '0';
            ch = getchar();
        }
        return ans * op;
    }
    const int maxn = 1e5 + 5;
    int sz[maxn],key[maxn],val[maxn],tot,ch[maxn][2],root;//key:堆相关;val:树相关 
    inline void update(int now)
    {
        sz[now] = sz[ls] + sz[rs] + 1;
    }
    void split(int now,int k,int &x,int &y)//x <= k,y > k
    {
        if(!now) 
        {        
            x = y = 0;
            return;
        }
        if(val[now] <= k) x = now,split(rs,k,rs,y);
        else y = now,split(ls,k,x,ls);
        update(now);
    }
    int merge(int x,int y)//val:x <= y 
    {
        if(!x || !y) return x + y;
        if(key[x] < key[y])
        {
            ch[x][1] = merge(ch[x][1],y);
            update(x);
            return x;
        }
        else 
        {
            ch[y][0] = merge(x,ch[y][0]);
            update(y);
            return y;
        }
    }
    int New(int k)
    {
        key[++tot] = rand(),val[tot] = k,sz[tot] = 1;
        return tot;
    }
    void insert(int k)
    {
        int x,y;
        split(root,k,x,y);
        root = merge(merge(x,New(k)),y);
    }
    void del(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 Rank(int k)
    {
        int x,y;
        split(root,k - 1,x,y);
        printf("%d
    ",sz[x] + 1);
        root = merge(x,y);
    }
    void fRank(int now,int k)
    {
        while(1)
        {
            if(k <= sz[ls]) now = ls;
            else if(k > sz[ls] + 1) k -= sz[ls] + 1,now = rs;
            else { printf("%d
    ",val[now]); return;}
        }
    }
    void pre(int k)
    {
        int x,y;
        split(root,k - 1,x,y);
        fRank(x,sz[x]);
        root = merge(x,y);
    }
    void suc(int k)
    {
        int x,y;
        split(root,k,x,y);
        fRank(y,1);
        root = merge(x,y);
    }
    int main()
    {
        int n = read();
        int opt,x;
        while(n--)
        {
            opt = read(),x = read(); 
            if(opt == 1) insert(x);
            if(opt == 2) del(x);
            if(opt == 3) Rank(x); 
            if(opt == 4) fRank(root,x);
            if(opt == 5) pre(x);
            if(opt == 6) suc(x); 
        }
        return 0;
    }

        

  • 相关阅读:
    sys模块
    反射
    动态导入模块
    类的静态属性
    多态
    继承
    组合
    linux系统各个子目录的内容
    mysql安装
    Docker 数据卷操作
  • 原文地址:https://www.cnblogs.com/LM-LBG/p/10331476.html
Copyright © 2011-2022 走看看