FHQ Treap【基于P3369的讲解】【随机数、各数组、函数运用】

版权声明:https://blog.csdn.net/qq_41730082 https://blog.csdn.net/qq_41730082/article/details/86601377

该模板题的题目链接


  很多人看到了FHQ Treap都不知道它是干什么用的,今天也是刚学的FHQ Treap,学了一整天了,终于过掉了洛谷的P3369了,也算是对这个算法有了些自己的了解,还是错的太多次,不然谁debug找到都快搞明白什么是FHQ Treap

  很多人都知道splay的平衡树、还有treap平衡树。但是普通的treap需要进行不断的反转,代码量着实有些大了,而且,优化的FHQ Treap还可以做到可持久化,所以,我就学习了FHQ Treap。

  FHQ Treap只有:(一)、分离(split)(二)、合并(merge)的功能「在这里由于merge函数名冲突,我利用mix()代替」。

讲一下FHQ Treap在本题中的几个应用:

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

那么应该怎么做这个?

先讲一下关于分离、合并函数:

分离函数split

void split(int now, int k, int &x, int &y)
{
    if(!now) x = y = 0;
    else
    {
        if(val[now] <= k)
        {
            x = now;
            split(rson[now], k, rson[now], y);
        }
        else
        {
            y = now;
            split(lson[now], k, x, lson[now]);
        }
        pushup(now);
    }
}

对于分离函数,我们要做到的就是找到那一半"<=K"的树,与">K"的那部分分开,分成两棵树。然后x得到的是小的那棵树,y得到大的那棵树。

合并函数merge(mix)

int mix(int x, int y)
{
    if(!x || !y) return x + y;
    if(pri[x] < pri[y])
    {
        rson[x] = mix(rson[x], y);
        pushup(x);
        return x;
    }
    else
    {
        lson[y] = mix(x, lson[y]);
        pushup(y);
        return y;
    }
}

合并两棵不同的树。

除去这两个,还有个迭代的就是求第K关系来用的:

int kth(int now, int k)
{
    while(true)
    {
        if(k <= siz[lson[now]]) now = lson[now];
        else if(k == siz[lson[now]] + 1) return now;
        else { k -= ( siz[lson[now]] + 1 ); now = rson[now]; }
    }
}

求now根下的第K小的点的位置的值。


接下来,就是求题目中的要求的问题了:

1.插入x数

            split(root, e1, x, y);
            root = mix(mix(x, new_node(e1)), y);

  想要插入一个大小为X的数,有不能更改它的二叉查找树的性质lson<now<rson的性质,我们就要以X的大小为判断要求,"<=e1"的那串数弄出来,然后再把另一头的树弄出来,然后再合并三棵子树。  

2.删除x数(若有多个相同的数,因只删除一个)

            split(root, e1, x, z);
            split(x, e1-1, x, y);
            y = mix(lson[y], rson[y]);
            root = mix(mix(x, y), z);

  删除一个值为X的数,由多个的话,仅删除一个,那么,不就是拆成3棵树,左边的树是"<x"的树,右边的是">x"的树,中间的树就是"==x"的树了,然后取它的头删除掉即可。

3.查询x数的排名(排名定义为比当前数小的数的个数+1+1。若有多个相同的数,因输出最小的排名)

            split(root, e1-1, x, y);
            printf("%d\n", siz[x] + 1);
            root = mix(x, y);

  想要查询X数的排名,无非就是找到"<X"的数的集合,然后"+1"即可。

4.查询排名为x的数

            printf("%d\n", val[kth(root, e1)]);

  找排名为X的数,就是要递归进去寻找了,然后后面有对应的函数。

5.求x的前驱(前驱定义为小于x,且最大的数)

            split(root, e1-1, x, y);
            printf("%d\n", val[kth(x, siz[x])]);
            root = mix(x, y);

  找到<x且最大的数,那么就是去"<x"的树里,找到树的最大值即可。

6.求x的后继(后继定义为大于x,且最小的数)

            split(root, e1, x, y);
            printf("%d\n", val[kth(y, 1)]);
            root = mix(x, y);

  类似与求前继的过程。


然后,今天先上一下具体的代码,明天继续修改,已经修改完成…… 请各大佬视察。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <cstdlib>
#include <ctime>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN = 1e5 + 7;
int N, op, lson[maxN], rson[maxN], val[maxN], pri[maxN], siz[maxN], sz;
inline void pushup(int x) { siz[x] = siz[lson[x]] + siz[rson[x]] + 1; }
int new_node(int v)
{
    siz[++sz] = 1;
    val[sz] = v;
    pri[sz] = rand();
    return sz;
}
int mix(int x, int y)
{
    if(!x || !y) return x + y;
    if(pri[x] < pri[y])
    {
        rson[x] = mix(rson[x], y);
        pushup(x);
        return x;
    }
    else
    {
        lson[y] = mix(x, lson[y]);
        pushup(y);
        return y;
    }
}
void split(int now, int k, int &x, int &y)
{
    if(!now) x = y = 0;
    else
    {
        if(val[now] <= k)
        {
            x = now;
            split(rson[now], k, rson[now], y);
        }
        else
        {
            y = now;
            split(lson[now], k, x, lson[now]);
        }
        pushup(now);
    }
}
int kth(int now, int k)
{
    while(true)
    {
        if(k <= siz[lson[now]]) now = lson[now];
        else if(k == siz[lson[now]] + 1) return now;
        else { k -= ( siz[lson[now]] + 1 ); now = rson[now]; }
    }
}
int main()
{
    srand((unsigned)time(NULL));
    scanf("%d", &N);
    sz = 0;
    int root = 0, x, y, z;
    memset(lson, 0, sizeof(lson));
    memset(rson, 0, sizeof(rson));
    while(N--)
    {
        scanf("%d", &op);
        int e1; scanf("%d", &e1);
        if(op == 1)
        {
            split(root, e1, x, y);
            root = mix(mix(x, new_node(e1)), y);
        }
        else if(op == 2)
        {
            split(root, e1, x, z);
            split(x, e1-1, x, y);
            y = mix(lson[y], rson[y]);
            root = mix(mix(x, y), z);
        }
        else if(op == 3)
        {
            split(root, e1-1, x, y);
            printf("%d\n", siz[x] + 1);
            root = mix(x, y);
        }
        else if(op == 4)
        {
            printf("%d\n", val[kth(root, e1)]);
        }
        else if(op == 5)
        {
            split(root, e1-1, x, y);
            printf("%d\n", val[kth(x, siz[x])]);
            root = mix(x, y);
        }
        else
        {
            split(root, e1, x, y);
            printf("%d\n", val[kth(y, 1)]);
            root = mix(x, y);
        }
    }
    return 0;
}
/*
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
 
 ans:
 106465
 84185
 492737
*/

猜你喜欢

转载自blog.csdn.net/qq_41730082/article/details/86601377
fhq
今日推荐