参考:https://www.bilibili.com/video/BV1rt411j7Ff?t=703大佬视频
一份代码,代码中有注释,对应着洛谷的**P3369 【模板】普通平衡树**
/*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{
int l,r,val,height,size;
}a[maxn];
int cnt,root;
inline void creat_node(int &now,int val){ //新创建一个结点
now=++cnt;
a[now].val=val;
a[now].size=1;
}
inline int get_height(int now){ //计算树的高度差,左-右
return a[a[now].l].height-a[a[now].r].height;
}
inline void update(int now){ //更新树的高度差以及元素个数
a[now].size=a[a[now].l].size+a[a[now].r].size+1;
a[now].height=max(a[a[now].l].height,a[a[now].r].height)+1;
}
inline void lrotate(int &now){ //左旋
int r=a[now].r; //把他右子树的结点暂存一下,一会需要以右孩子为根节点
a[now].r=a[a[now].r].l; //把根节点右子树的左子树挂在原来根节点的上右子树上
a[r].l=now; //把原来的根节点挂在原来的右子树(现在的根节点)上去
now=r; //现在的根节点更新
update(a[now].l),update(now); //检查树是否需要更新
}
inline void rrotate(int &now){ //右旋
int l=a[now].l;
a[now].l=a[a[now].l].r;
a[l].r=now;
now=l;
update(a[now].r),update(now);
}
inline void check(int &now){ //检查树是否需要旋转
int nh=get_height(now);
if(nh>1){ //如果左边-右边高度大于1
int lh=get_height(a[now].l);
if(lh>0) rrotate(now); //LL情况
else lrotate(a[now].l),rrotate(now); //LR情况
}
else if(nh<-1){
int rh=get_height(a[now].r);
if(rh<0) lrotate(now); //RR情况
else rrotate(a[now].r),lrotate(now); //RL情况
}
else if(now) update(now);
}
inline void insert(int &now,int val){ //插入
if(!now) creat_node(now,val); //如果没有结点,插入
else if(val<a[now].val) insert(a[now].l,val); //搜索树的性质,小了往左差,大了往右插
else insert(a[now].r,val);
check(now);
}
inline int ifind(int &now,int fa){ //查找后继点
int ret;
if(!a[now].l){
ret=now;
a[fa].l=a[now].r;
}
else{
ret=ifind(a[now].l,now);
check(now);
}
return ret;
}
inline void del(int &now,int val){ //删稠某点
if(val==a[now].val){
int l=a[now].l,r=a[now].r;
if(!l||!r) now=l+r; //两儿子都没有或者是只有一个儿子
else{
now=ifind(r,r); //找后继
if(now!=r) a[now].r=r; //如果后继是他本身
a[now].l=l;
}
}
else if(val<a[now].val) del(a[now].l,val);
else del(a[now].r,val);
check(now);
}
inline int get_rank(int val){ //找出某个数的名次
int now=root,rank=1;
while (now){
if(val<=a[now].val) now=a[now].l; //往树的左孩子走
else{
rank+=a[a[now].l].size+1; //往树的右孩子走,同时减去加上他小的个数(他的左子树的数都比他小,所以他的总排名肯定要比左边的所有数都高,所以先用rank加上左数大小)
now=a[now].r; //更新目前的根节点
}
}
return rank;
}
inline int get_num(int rank){ //找出名次的数字
int now=root;
while (now){
if(a[a[now].l].size+1==rank) break; //如果找到了直接break
else if(a[a[now].l].size>=rank) now=a[now].l; //同理
else{
rank-=a[a[now].l].size+1; //往树的右孩子走,同时减去比他小的个数(他的左子树的数都比他小,所以直接减去左子树的大小)
now=a[now].r;
}
}
return a[now].val;
}
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++){
int ch,x;
ch=read(),x=read();
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;
}