//Treap版本平衡树
#include
#include
#include<string>
#define WR WinterRain
using namespace std;
const int WR=1001000,INF=1e9+7;
struct BalanceTree{
int son[2],fa;//son记录左右儿子
//0是左1是右
int val,sze,cnt,rnk;
BalanceTree(){son[0]=son[1]=val=fa=sze=rnk=cnt=0;}
}tree[WR];
int n;
int root,tot;
int read(){
int s=0,w=1;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<1)+(s<<3)+ch-48;
ch=getchar();
}
return s*w;
}
int add_point(int v){//新建一个节点
tree[++tot].val=v;//赋值
tree[tot].rnk=rand();//随机排名
tree[tot].sze=1;//大小为1
tree[tot].cnt=1;//当前节点对应数的个数(副本数)是1
//比如说有3个大小为1的数,1对应节点的cnt就为3
return tot;
}
void pushup(int k){
tree[k].sze=tree[tree[k].son[0]].sze+tree[tree[k].son[1]].sze+tree[k].cnt;
//本节点子树大小=左儿子子树大小+右儿子子树大小+本节点副本数
}
void build(){
root=add_point(-INF);
tree[root].son[1]=add_point(INF);//先加入正无穷和负无穷,便于之后操作
//因为INF>-INF,所以INF是右子树
pushup(root);
}
void rotat(int &id,int dir){//dir表示方向,0是左旋1是右旋
int tmp=tree[id].son[dir^1];//旋转
tree[id].son[dir^1]=tree[tmp].son[dir];
tree[tmp].son[dir]=id;
id=tmp;
pushup(tree[id].son[dir]);//旋转以后size会改变,只更新自己和转上来的点
//pushup一下,注意先子节点再父节点
pushup(id);//旋转实质是在满足BST的性质的基础上比较优先级,
//然后通过交换本节点和其某个叶子节点把链叉开成二叉形状(从而控制深度)
}
void insrt(int &id,int v){
if(!id){
id=add_point(v);
return;
}
if(v==tree[id].val) tree[id].cnt++;//直接++
else{
int dir;
if(v0;
else dir=1;//二叉搜索树,类似于二叉堆
insrt(tree[id].son[dir],v);//递归建树
if(tree[id].rnk1);
//与左节点交换要右旋,右节点交换左旋
}
pushup(id);
}
void remov(int &id,int v){//删除
if(!id) return;//发现这个节点本来就不存在
if(v==tree[id].val){
if(tree[id].cnt>1){
tree[id].cnt--;
pushup(id);
return;//若副本不止一个,减去一个就好
}
if(tree[id].son[0]||tree[id].son[1]){
//发现只有一个值,且有儿子节点,我们只能把值旋转到底部删除
if(!tree[id].son[1]||tree[tree[id].son[0]].rnk>tree[tree[id].son[1]].rnk){
//当前点被移走之后,会有一个新的点补上来(左儿子或右儿子)
//如果没有一边的儿子直接补另一边
//否则按照优先级,优先级大的补上来
rotat(id,1);remov(tree[id].son[1],v);
//我们会发现,右旋是与左儿子交换,当前点变成右节点
}else{
rotat(id,0);remov(tree[id].son[0],v);
//左旋则是与右儿子交换,当前点变为左节点
}
pushup(id);
return;
}
id=0;return;//发现这个节点是叶子节点,删掉完事
}
if(v0],v);
else remov(tree[id].son[1],v);
pushup(id);//继续删除,最后更新
}
int get_rank(int id,int v){
//printf("%d\n",111);
if(!id) return 0;
if(v==tree[id].val) return tree[tree[id].son[0]].sze+1;
//查询到该值,由BST性质可知:该点左边值都比该点的值(查询值)小,故rank为左儿子大小+1
if(vreturn get_rank(tree[id].son[0],v);
//如果小于,递归查询
else return get_rank(tree[id].son[1],v)+tree[id].cnt+tree[tree[id].son[0]].sze;
//若查询值大于该点值,说明询问点在当前点的右侧,且此点左子树的值都小于查询值
//所以要加上cnt[id]和左子树大小
}
int get_val(int id,int rk){
if(!id) return INF;//向右找找不到说明是正无穷
if(rk<=tree[tree[id].son[0]].sze) return get_val(tree[id].son[0],rk);
//根节点排名已经大于rank了,说明rank对应的值在左儿子那里
else if(rk<=tree[id].cnt+tree[tree[id].son[0]].sze) return tree[id].val;
//上一步排除了在左区间的情况,若是rank在左与中(目前节点)中,则直接返回目前节点(中区间)的值
else return get_val(tree[id].son[1],rk-tree[tree[id].son[0]].sze-tree[id].cnt);
//剩下只能在右区间找了,rank减去左区间大小和根节点大小,继续递归
}
int get_pre(int v){//找前驱
int id=root,pre;
while(id){
if(tree[id].val1];
//满足当前节点比目标小,往当前节点的右侧寻找最大值(最小值最大)
else id=tree[id].son[0];
//无论是比目标节点大还是等于目标节点,都不满足前驱条件,应往更小处靠近
}
return pre;
}
int get_nxt(int v){//找后继
int id=root,nxt;
while(id){
if(tree[id].val>v) nxt=tree[id].val,id=tree[id].son[0];
//同理,满足条件向左寻找最小值(最大值最小)
else id=tree[id].son[1];
}
return nxt;
}
int main(){
build();
n=read();
for(int i=1;i<=n;i++){
int opt=read(),x=read();
if(opt==1) insrt(root,x);
if(opt==2) remov(root,x);
if(opt==3) printf("%d\n",get_rank(root,x)-1);
//注意:因为初始化时插入了INF和-INF,所以查询排名时要减1(-INF不是第一小,是“第零小”)
if(opt==4) printf("%d\n",get_val(root,x+1));
if(opt==5) printf("%d\n",get_pre(x));
if(opt==6) printf("%d\n",get_nxt(x));
}
return 0;
}