//平衡树 Treap
//维护一个堆使得随机权值小(大)的数始终在上方
//使用随机权值目的:防止出题人卡
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
struct uio{
int l,r,siz,num,rd,tim;//左儿子,右儿子,子树大小+自己大小,点值,随机权值,出现次数
}treap[100001];//此代码为小根堆 即随机权值小的在上方
int n,size,ans,root;//size树的大小
void update(int k)//更新
{
treap[k].siz=treap[treap[k].l].siz+treap[treap[k].r].siz+treap[k].tim;
}
void right(int &k)//右旋
{
int x=treap[k].l;
treap[k].l=treap[x].r;
treap[x].r=k;
treap[x].siz=treap[k].siz;
update(k);
k=x;
}
void left(int &k)//左旋
{
int x=treap[k].r;
treap[k].r=treap[x].l;
treap[x].l=k;
treap[x].siz=treap[k].siz;
update(k);
k=x;
}
void insert(int &k,int x)
{
if(k==0)//到达叶节点就开始插入
{
size++;
k=size;
treap[k].siz=1;
treap[k].tim=1;
treap[k].num=x;
treap[k].rd=rand();
return;
}
treap[k].siz++;//插入节点的每个父节点子树大小都加一
if(treap[k].num==x)//若已有此节点
treap[k].tim++;//出现次数加一
else
{
if(x>treap[k].num)//在右子树中
{
insert(treap[k].r,x);
if(treap[treap[k].r].rd<treap[k].rd)//子节点随机数比父节点小
left(k);//左旋
}
else//在右子树中
{
insert(treap[k].l,x);
if(treap[treap[k].l].rd<treap[k].rd)//子节点随机数比父节点小
right(k);//右旋
}
}
}
void del(int &k,int x)
{
if(k==0)//树中已无节点
return;
if(treap[k].num==x)//找到了
{
if(treap[k].tim>1)//出现次数大于一,直接删除即可
{
treap[k].tim--;
treap[k].siz--;
return;
}
if(treap[k].l*treap[k].r==0)//左右子树有一棵为空
k=treap[k].l+treap[k].r;//直接把非空子树接在原树上
else
{
if(treap[treap[k].l].rd<treap[treap[k].r].rd)//每次下沉时与随机权值小的交换 以确保小根堆性质不变
right(k),del(k,x);
else left(k),del(k,x);
}
}
else//没找到
{
if(x>treap[k].num)//在右子树中
{
treap[k].siz--;
del(treap[k].r,x);
}
else//在左子树中
{
treap[k].siz--;
del(treap[k].l,x);
}
}
}
int get_no(int k,int x)
{
if(k==0)//树中没有节点
return 0;
if(treap[k].num==x)//找到x
return treap[treap[k].l].siz+1;//结果为自己加左子树的大小
else if(x>treap[k].num)//在右子树
return treap[treap[k].l].siz+treap[k].tim+get_no(treap[k].r,x);//结果为递归回的结果加自己的大小加左子树的大小
else return get_no(treap[k].l,x);//在左子树 结果为递归回的结果
}
int get_num(int k,int x)
{
if(k==0)//树中没有节点
return 0;
if(x<=treap[treap[k].l].siz)//在左子树中
return get_num(treap[k].l,x);
else if(x>(treap[treap[k].l].siz+treap[k].tim))//在右子树中
return get_num(treap[k].r,x-treap[treap[k].l].siz-treap[k].tim);
else return treap[k].num;//在这个点上
}
void pre(int k,int x)
{
if(k==0)
return;
if(treap[k].num<x)//在右子树中
{
ans=k;
pre(treap[k].r,x);
}
else pre(treap[k].l,x);//在左子树中
}
void nxt(int k,int x)
{
if(k==0)
return;
if(treap[k].num>x)//在左子树中
{
ans=k;
nxt(treap[k].l,x);
}
else nxt(treap[k].r,x);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
if(u==1)
insert(root,v);
if(u==2)
del(root,v);
if(u==3)
printf("%d\n",get_no(root,v));
if(u==4)
printf("%d\n",get_num(root,v));
if(u==5)
{
pre(root,v);
printf("%d\n",treap[ans].num);
}
if(u==6)
{
nxt(root,v);
printf("%d\n",treap[ans].num);
}
}
return 0;
}