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\n",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\n",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\n",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\n",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;
}

 

    

转载于:https://www.cnblogs.com/LM-LBG/p/10331476.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值