参考视频:https://www.bilibili.com/video/BV1wt411u7xL?t=1142讲的特别好!
注释都在代码中了
/*Keep on going Never give up*/
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
const int maxn = 2e5+10;
const int MaxN = 0x3f3f3f3f;
const int MinN = 0xc0c0c00c;
typedef long long ll;
const int mod = 100000000;
using namespace std;
struct node{ //这里记录重复结点用的是cnt这个变量1代表这个数有一个,2代表这个数有两个。。。
int l,r;
int val,size,cnt;
}spl[maxn];
int cnt,root;
void new_node(int &now,int &val){ //新建结点
now=++cnt;
spl[now].val=val;
spl[now].size++;
spl[now].cnt++;
}
void update(int now){ //更新size
spl[now].size=spl[spl[now].l].size+spl[spl[now].r].size+spl[now].cnt;
}
void zig(int &now){ //右旋
int l=spl[now].l;
spl[now].l=spl[l].r;
spl[l].r=now;
now=l;
update(spl[now].r),update(now);
}
void zag(int &now){ //左旋
int r=spl[now].r; //把他右子树的结点暂存一下,一会需要以右孩子为根节点
spl[now].r=spl[r].l; //把根节点右子树的左子树挂在原来根节点的上右子树上
spl[r].l=now; //把原来的根节点挂在原来的右子树(现在的根节点)上去
now=r; //现在的根节点更新
update(spl[now].l),update(now); //检查树是否需要更新
}
void splaying(int x,int &y){ //
if(x==y) return; //如果到了我们指定的结点,return
//主要是通过递归进行伸展操作,
//通过从根节点不断递归到自己想要悬上去的结点
//不过缺点就是常数可能比较大,但是好写一些
int &l=spl[y].l,&r=spl[y].r;
if(x==l) zig(y);
else if(x==r) zag(y);
else{
if(spl[x].val<spl[y].val){
if(spl[x].val<spl[l].val)
splaying(x,spl[l].l),zig(y),zig(y); //zigzig情况
else splaying(x,spl[l].r),zag(l),zig(y); //zagzig情况
}
else{
if(spl[x].val>spl[r].val)
splaying(x,spl[r].r),zag(y),zag(y); //zagzag情况
else splaying(x,spl[r].l),zig(r),zag(y); //zigzag情况
}
}
}
void del_node(int now){ //找到删除的结点并将其删除
splaying(now,root); //先把要删除的结点延申到根节点
//看看次数是否重复了,如果重复了结点直接-1即可(看内存池是如何定义的)
if(spl[now].cnt>1){
spl[now].size--;
spl[now].cnt--;
}
else if(spl[root].r){ //找一下后继,建议看一下up的视频,讲的很清晰(17min开始看)
int temp=spl[root].r;
while (spl[temp].l) temp=spl[temp].l;
splaying(temp,spl[root].r);
spl[spl[root].r].l=spl[root].l;
root=spl[root].r;
update(root);
}
else root=spl[root].l;
}
void del(int &now,int val){ //找到那个删除的结点
if(spl[now].val==val) del_node(now);
else if(val<spl[now].val) del(spl[now].l,val);
else del(spl[now].r,val);
}
void insert(int &now,int val){ //插入
if(!now) new_node(now,val),splaying(now,root); //新建结点并插入
else if(val<spl[now].val) insert(spl[now].l,val);
else if(val>spl[now].val) insert(spl[now].r,val);
else spl[now].size++,spl[now].cnt++,splaying(now,root);
}
int get_rank(int val){
int now=root,rank=1;
//往树的右孩子走,同时减去加上他小的个数
//(他的左子树的数都比他小,所以他的总排名肯定要比左边的所有数都高,
//所以先用rank加上左数大小)
while (now){
if(spl[now].val==val){
rank+=spl[spl[now].l].size;
splaying(now,root);
break;
}
if(val<=spl[now].val) now=spl[now].l;
else{
rank+=spl[spl[now].l].size+spl[now].cnt;
now=spl[now].r;
}
}
return rank;
}
int get_num(int rank){
int now=root;
while (now){
int lsize=spl[spl[now].l].size;
if(lsize+1<=rank&&rank<=lsize+spl[now].cnt){ //如果找到了直接break
splaying(now,root);
break;
}
else if(lsize>=rank) now=spl[now].l;
else{
//往树的右孩子走,同时减去比他小的个数
//(他的左子树的数都比他小,所以直接减去左子树的大小)
rank-=lsize+spl[now].cnt;
now=spl[now].r;
}
}
return spl[now].val;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++){
int ch,x;
scanf("%d%d",&ch,&x);
if(ch==1) insert(root,x);
if(ch==2) del(root,x);
if(ch==3) printf("%d\n",get_rank(x));
if(ch==4) printf("%d\n",get_num(x));
if(ch==5) printf("%d\n",get_num(get_rank(x)-1));
if(ch==6) printf("%d\n",get_num(get_rank(x+1)));
}
return 0;
}