luogu-p3369(fhq-treap)

题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. xx 数
  2. xx 数(若有多个相同的数,因只删除一个)
  3. xx 数的排名(排名定义为比当前数小的数的个数 +1+1 )
  4. 排名为 xx 的数
  5. xx 的前驱(前驱定义为小于 xx,且最大的数)
  6. xx 的后继(后继定义为大于 xx,且最小的数)

输入格式
第一行为 nn,表示操作的个数,下面 nn 行每行有两个数 opt 和 x,opt 表示操作的序号(1<=opt<=6),

输出格式
对于操作 3,4,5,6 每行输出一个数,表示对应答案

输入

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

输出

106465
84185
492737

说明/提示
【数据范围】
对于 100%100% 的数据, 1 < = n < = 1 0 5 1<=n<=10^5 x < = 1 0 7 |x|<=10^7 ,
来源:Tyvj1728 原名:普通平衡树
在此鸣谢

附上一个大佬的B站链接:
https://www.bilibili.com/video/av60140850?from=search&seid=17315401495564564850
看题目我们知道这是一道fhq-treap的模板题,当然treap树也能做,首先我们来讲fhq-treap(无旋treap)叭。
顾名思义,无旋treap是不需要旋转的,fhq-treap主要是在分裂(将一棵平衡树分成两棵平衡树)和合并(将两颗平衡树合并为一棵平衡树)这两个操作上进行。
分裂(split)
这里主要介绍按值分裂,即分成的两颗子树中一棵子树的所有值都小于等于val,另一棵子树的所有值都大于val,具体操作请看代码,
split:

inline void split(int now,int val,int &x,int &y)
{
    if(!now){//如果这个节点不存在,那么不存在子树
        x = y = 0;
        return;
    }
    if(fhq[now].val<=val){//如果该根节点属于x子树
        x = now;
        split(fhq[now].r, val, fhq[now].r, y);//那么该树的左子树属于x,继续判断右子树
    }
    else{//如果该根节点属于y子树
        y = now;
        split(fhq[now].l, val, x, fhq[now].l);////那么该树的右子树属于y,继续判断左子树
    }
    update(now);
}

合并(merge)
合并是将两棵子树合并为一棵树,是一个递归的过程,合并时,我们保证x子树的所有节点都小于y子树,在合并之后我们需要保证得到的树是一个二叉堆,即每个根节点的优先级大于等于它的儿子结点。
merge:

inline int merge(int x,int y)
{
    if(!x||!y)
        return x + y;
    else{
        if(fhq[x].key>fhq[y].key){
            fhq[x].r = merge(fhq[x].r, y);
            update(x);
            return x;
        }
        else{
            fhq[y].l = merge(x, fhq[y].l);
            update(y);
            return y;
        }
    }
}

然后我们就可以在这两个操作的基础上做treap的基本操作啦。
插入
如果我们要插入一个值为val的结点,我们可以把现有的树按值(val)分裂成两棵子树,权值小的子树的根节点为x,权值大的子树的根节点为y,然后我们要得到加入值为val的结点只需要将新建的点和两棵子树合并即可。
insert:

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

删除
如果我们想要删除一个值为val的点,我们可以把现有的树分为三棵子树,一棵是以x为根节点的所有的权值都小于val的子树,一棵是以y为结点的所有的权值都等于val的子树,最后一棵是以z为结点的所有的权值都大于val的子树,那么这个时候我们要删除一个值为val的点,我们只需要删除掉子树y的根节点即可,即将y的左子树和y的右子树合并起来。
del:

inline void del(int val)
{
    split(root, val, x, z);
    split(x, val - 1, x, y);
    y = merge(fhq[y].l, fhq[y].r);
    root = merge(merge(x, y), z);
}

求val的排名
由题意可知,值val的排名=所有权值小于val的结点数+1.所以我们将这棵树按值val-1分裂,值val的排名就等于子树x的结点数+1。
getrank:

inline void getrank(int val)
{
    split(root, val - 1, x, y);
    printf("%d\n", fhq[x].size + 1);
    root = merge(x, y);
}

求排名为x的值val
我们可以通过二分法来一点一点遍历得到答案,如果根节点为root的树的左子树的结点个数大于等于x,那么答案就在左子树中,我们去遍历左子树,如果根节点的左子树的结点个数等于x-1,那么答案就是根节点,如果根节点的左子树的结点个数小于x-1,那么答案在右子树中,我们寻找在右子树中排名第x-fhq[fhq[now].l].size-1的结点的值。
getnum:

inline void getnum(int ran)
{
    int now = root;
    while(now){
        if(fhq[fhq[now].l].size+1==ran){
            break;
        }
        else if(fhq[fhq[now].l].size>=ran){
            now = fhq[now].l;
        }
        else{

            ran -= fhq[fhq[now].l].size + 1;
            now = fhq[now].r;
        }
    }
    printf("%d\n", fhq[now].val);
}

寻找val的前驱
前驱就是小于val的最大值,我们将现有的子树按值(val-1)分裂为两棵子树,我们在权值较小的子树中一直遍历右子树即可。
pre:

inline void pre(int val)
{
    split(root, val - 1, x, y);
    int now = x;
    while(fhq[now].r){
        now = fhq[now].r;
    }
    printf("%d\n", fhq[now].val);
    root = merge(x, y);
}

寻找val的后继
后继定义为大于val的最小值,如果我们将子树按值(val)分裂为两棵子树,我们在权值较大的子树中找到最左边的结点就是答案了。
nex:

inline void nex(int val)
{
    split(root, val, x, y);
    int now = y;
    while(fhq[now].l){
        now = fhq[now].l;
    }
    printf("%d\n", fhq[now].val);
    root = merge(x, y);
}

代码:

#include <bits/stdc++.h>
using namespace std;
std::mt19937 rnd(233);
const int maxn=1e5+7;
struct treap
{
    int l, r, val, key, size;
} fhq[maxn];
int cnt, root;
inline int newnode(int val)
{
    fhq[++cnt].val = val;
    fhq[cnt].size = 1;
    fhq[cnt].key=rnd();
    return cnt;
}
void update(int x)
{
    fhq[x].size = fhq[fhq[x].l].size + fhq[fhq[x].r].size+1;
}
inline void split(int now,int val,int &x,int &y)
{
    if(!now){
        x = y = 0;
        return;
    }
    if(fhq[now].val<=val){
        x = now;
        split(fhq[now].r, val, fhq[now].r, y);
    }
    else{
        y = now;
        split(fhq[now].l, val, x, fhq[now].l);
    }
    update(now);
}
inline int merge(int x,int y)
{
    if(!x||!y)
        return x + y;
    else{
        if(fhq[x].key>fhq[y].key){
            fhq[x].r = merge(fhq[x].r, y);
            update(x);
            return x;
        }
        else{
            fhq[y].l = merge(x, fhq[y].l);
            update(y);
            return y;
        }
    }
}
int x, y, z;
inline void insert(int val)
{
    z = newnode(val);
    split(root, val, x, y);
    root = merge(merge(x, z), y);
}
inline void del(int val)
{
    split(root, val, x, z);
    split(x, val - 1, x, y);
    y = merge(fhq[y].l, fhq[y].r);
    root = merge(merge(x, y), z);
}
inline void getrank(int val)
{
    split(root, val - 1, x, y);
    printf("%d\n", fhq[x].size + 1);
    root = merge(x, y);
}
inline void getnum(int ran)
{
    int now = root;
    while(now){
        if(fhq[fhq[now].l].size+1==ran){
            break;
        }
        else if(fhq[fhq[now].l].size>=ran){
            now = fhq[now].l;
        }
        else{

            ran -= fhq[fhq[now].l].size + 1;
            now = fhq[now].r;
        }
    }
    printf("%d\n", fhq[now].val);
}
inline void pre(int val)
{
    split(root, val - 1, x, y);
    int now = x;
    while(fhq[now].r){
        now = fhq[now].r;
    }
    printf("%d\n", fhq[now].val);
    root = merge(x, y);
}
inline void nex(int val)
{
    split(root, val, x, y);
    int now = y;
    while(fhq[now].l){
        now = fhq[now].l;
    }
    printf("%d\n", fhq[now].val);
    root = merge(x, y);
}
int main()
{
#ifndef ONLINE_JUDGE
    //freopen("in.in", "r", stdin);
    //freopen("out.out", "w", stdout);
#endif
    clock_t c1 = clock();
    int n;
    cin >> n;
    int opt, x;
    while(n--){
        int opt, x;
        cin >> opt >> x;
        //cout << "n=" << n << endl;
        switch(opt){
            case 1:
                insert(x);
                break;
            case 2:
                del(x);
                break;
            case 3:
                getrank(x);
                break;
            case 4:
                getnum(x);
                break;
            case 5:
                pre(x);
                break;
            case 6:
                nex(x);
                break;
        }
    }
    return 0;
}
发布了34 篇原创文章 · 获赞 3 · 访问量 237

猜你喜欢

转载自blog.csdn.net/qq_44641782/article/details/103649993
今日推荐