#include <iostream>
#include <cstdio>
#include <time.h>
using namespace std;
const int N = 1E5;
int idx, root;
//FHQ_Treap核心思想:
//构建一个既有BST结构,又有堆结构的树
//通过以v为键值分裂,与 以key为键值合并 两个操作操作维护BST与堆结构
//从而用两个键值使树尽量分散,保证高效的搜索效率
//同时可以用val值作为关键分裂树,来插入、删除、查找结点,前驱,后驱,获取第k位排名的结点
struct node{
int l, r;
int val;
int rnd;
int size;
} tr[N];
void newnode(int&x,int v){
x = ++idx;
tr[x].val = v;
tr[x].rnd = rand();
tr[x].size = 1;
}
void pushup(int p){
tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + 1;
}
//按值分裂
//把一棵树分为两棵树
//一棵树的val<=v
//另一棵树的val>v
//x为指向左Treap的指针
//y为指向右Treap的指针
void split(int p,int v,int &x,int &y){
//如果p=0,递归到了空节点,返回
if(!p){
x = y = 0;
return;
}
//如果当前结点p的val<=v
//说明p及其左子树都属于分裂后的左Treap
//而p的右子树部分可能也有<=v的结点
//继续递归分裂右子树
//把<=v的部分作为p的右子树
//同时把x指向左Treap的根
if(tr[p].val<=v){
//x指向左Treap的根
x = p;
//递归分裂右子树
//x变为了tr[x].r
//用来找新的tr[x].r
split(tr[x].r, v, tr[x].r, y);
pushup(x);
}
else{
y = p;
split(tr[y].l, v, x, tr[y].l);
pushup(y);
}
}
//合并
//对两棵树
//按照key值合并
//我们保证前面这棵子树的valmax<=后面这棵子树的valmin
//所以只能用分裂出来的树进行合并
//同时我们保证合并的两棵树为堆的结构
//这样子,我们可以保证根据key值合并以后的树,它的val值仍然保持BST结构
//我们只需要考虑把哪棵树放在上面
//哪棵树放在下面
//将key小的放在上面
//返回根的编号
int merge(int x,int y){
//如果x树为空或y树为空
//直接返回x树或y树的编号
if(!x||!y) return x + y;
//x树的rnd值比y树的ran值小
//将y加入到x的右子树中
//递归比较x的右儿子与y的rnd值大小
//x是根,返回x树的编号
if(tr[x].rnd<tr[y].rnd){
//因为x的val值一定小于y的val值,
//所以y树只能接到x的右子树上
tr[x].r = merge(tr[x].r, y);
pushup(x);
return x;
}
//同理
else{
//由于y的val值一定大于x的val值
//所以x树只能接到y的左子树上
tr[y].l = merge(x, tr[y].l);
pushup(y);
return y;
}
}
//插入
//按照插入值分裂为x,y两棵树
//再新建一个结点,把树合并回去
void insert(int v){
int x, y, z;
//按照v值分裂,x指向val<=v的树
//y指向val>v的树
split(root, v, x, y);
//创建val值为v的新结点
newnode(z, v);
//把新结点和x,y合并
root = merge(merge(x, z), y);
}
//删除
//和插入类似
//先按删除值v分裂为x,z两棵树
//则x的值<=val
//z的值>val
//再按删除值v-1把x分裂为x',y两棵树
//则x'的值<=val-1
//y的值>val-1且<=val——要么是val,要么y为空
//合并y的左右子树为y'
//(相当于删除根节点——一次只删除一个结点)
//最后合并x',y',z
void del(int v){
int x, y, z;
split(root, v, x, z);
split(x, v - 1, x, y);
y = merge(tr[y].l, tr[y].r);
root = merge(merge(x, y), z);
}
//找第k大的节点
int get_k(int p,int k){
//如果是自己
if(k==tr[tr[p].l].size+1){
return tr[p].val;
}
else if(k<=tr[tr[p].l].size){
return get_k(tr[p].l, k);
}
else{
k -= tr[tr[p].l].size + 1;
return get_k(tr[p].r, k);
}
}
//找val的排名
//按照val-1将树分裂为x和y
//则x<=val-1
//y>val
//那么x的size+1就是排名
//然后再把x和y合并
int getrank(int v){
int x, y;
split(root, v - 1, x, y);
int ans = tr[x].size + 1;
root = merge(x, y);
return ans;
}
//找前驱:
//按照val-1分裂为x,y两棵树
//在x内找最大值输出,然后合并
int getpre(int v){
int x, y, s, ans;
split(root, v - 1, x, y);
//x的值<=val-1
//y的值>val
//s为x树的大小,也就是前驱的排名
s = tr[x].size;
//找到排名为s的结点就是找到了前驱
ans = get_k(x, s);
root = merge(x, y);
return ans;
}
//找后驱:
//按照val分裂为x,y两棵树,在y内找最小值输出,然后合并
int getnxt(int v){
int x, y, ans;
split(root, v, x, y);
//最小值就是在y树中排名为1的结点
ans = get_k(y, 1);
root=merge(x,y);
return ans;
}
11-05
917