参考视频:https://www.bilibili.com/video/BV1ft411E7JW?t=1780
代码中有较为详细的注释
#include <bits/stdc++.h>
using namespace std;
int cnt,root;
mt19937 rnd(233); //随机数
struct Node{
int l,r,val,key,size;
}fhq[100010];
int newnode(int val){ //开辟新结点
fhq[++cnt].val=val;
fhq[cnt].key=rnd();
fhq[cnt].size=1;
return cnt; //直接返回一个新结点
}
void update(int now){ //push_up更新节点大小(当加入了新结点)
fhq[now].size=fhq[fhq[now].l].size+fhq[fhq[now].r].size+1;
}
void spilt(int now,int val,int &x,int &y){ //分裂乘两棵树
if(!now) x=y=0; //如果是一颗空树,直接返回x,y是空树即可
else{
if(fhq[now].val<=val){ //这里是按值分裂把小于等于val值得分到x树,大于val得分到y树
x=now; //这个值左孩子上得所有结点都属于x这棵树
spilt(fhq[now].r,val,fhq[now].r,y);
//右孩子上大部分都会大于这个值,
//也有可能会有小于val这个值得,所以我们要继续递归右子树进行查找
}
else{
y=now; //复读机
spilt(fhq[now].l,val,x,fhq[now].l);
}
update(now);
}
}
int merge(int x,int y){ //把两棵树合并起来
if(!x||!y) return x+y;
if(fhq[x].key>fhq[y].key){ //既要符合堆得性质,又要符合搜索树得性质
//我们知道x这棵树上得值全都小于y这颗树上得值,
// 所以当x得key大于y这个key得时候,y在x得右下方(既要在右边,也要在下面)
//所以让x得右子树跟y合并
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;
void insert(int val){ //插入某值
spilt(root,val,x,y); //先把树按照val值分裂开
//因为x树都小于等于val,所以我们直接让新结点跟x合并起来
//再把x树跟y树合并起来
root=merge(merge(x,newnode(val)),y);
}
void del(int val){ //删除(此del不可以删除树上没有得值)
//先按照val将树分裂开
//再把x树按照val-1分裂成x,y树
//那么y树上所有得值都等于val
spilt(root,val,x,z);
spilt(x,val-1,x,y);
//直接把y得根节点去掉即可,也就是把左右子树合并
y=merge(fhq[y].l,fhq[y].r);
root=merge(merge(x,y),z);//最后把他们合并起来即可
}
int get_rank(int val){ //排名
spilt(root,val-1,x,y);
//按照val-1把树分为xy,那么x上树所有值都小于val
//那么x树上得根节点得size即为比他小得数得个数,再+1即可
int temp=fhq[x].size+1;
root=merge(x,y); //不要忘记把树还原
//cout<<fhq[x].size<<endl;
return temp;
}
int get_num(int rank){
int now=root;
while (now){
if(fhq[fhq[now].l].size+1==rank) break; //如果找到直接返回结点值
//如果搜索过得数大于rank,那么我们要往左数搜索
//因为右子树的值已经大于rank了,所以答案一定不在右子树上
else if(fhq[fhq[now].l].size>=rank) now=fhq[now].l;
//及时减去已经走过的排名,类似于主席树第k大思想
else{
rank-=fhq[fhq[now].l].size+1;
now=fhq[now].r;
}
}
return fhq[now].val;
};
int pre(int val){
//找前驱,按照val-1分裂此树
//在他前面的哪个数,一定是在x树上的最右端(最大值)
spilt(root,val-1,x,y);
int now=x;
while (fhq[now].r) now=fhq[now].r;
int temp=fhq[now].val;
root=merge(x,y);
return temp;
}
int nxt(int val){
//找后继,按照val分裂此树
//在他后面的树一定是y树上最左边的值(最小值)
spilt(root,val,x,y);
int now=y;
while (fhq[now].l) now=fhq[now].l;
int temp=fhq[now].val;
root=merge(x,y);
return temp;
}
void check(int now){ //中序遍历
if(!now) return;
check(fhq[now].l);
printf("%d ",fhq[now].val);
check(fhq[now].r);
}
int main() {
int n;
cin>>n;
while (n--){
int opt,x;
scanf("%d%d",&opt,&x);
if(opt==1) insert(x);
if(opt==2) del(x);
if(opt==3) printf("%d\n",get_rank(x));
if(opt==4) printf("%d\n",get_num(x));
if(opt==5) printf("%d\n",pre(x));
if(opt==6) printf("%d\n",nxt(x));
// check(root);
// cout<<endl;
}
return 0;
}