该模板题的题目链接
很多人看到了FHQ Treap都不知道它是干什么用的,今天也是刚学的FHQ Treap,学了一整天了,终于过掉了洛谷的P3369了,也算是对这个算法有了些自己的了解,还是错的太多次,不然谁debug找到都快搞明白什么是FHQ Treap。
很多人都知道splay的平衡树、还有treap平衡树。但是普通的treap需要进行不断的反转,代码量着实有些大了,而且,优化的FHQ Treap还可以做到可持久化,所以,我就学习了FHQ Treap。
FHQ Treap只有:(一)、分离(split);(二)、合并(merge)的功能「在这里由于merge函数名冲突,我利用mix()代替」。
讲一下FHQ Treap在本题中的几个应用:
- 插入x数
- 删除x数(若有多个相同的数,因只删除一个)
- 查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)
- 查询排名为x的数
- 求x的前驱(前驱定义为小于x,且最大的数)
- 求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
*/