#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0);cout.tie(0);cin.tie(0);
#define endl '\n'
#define int long long
const int N=1e6+114;
const double alpha=0.7;
struct Node{
int ls,rs;
int val;
int tot;//当前节点子树占的空间总量,包含有储存与已经删除的节点
int size;//当前节点子树s实际存储数量
int flag;//是否有数字
}t[N];
int order[N],cnt;//存拍平后的结果,cnt是数量
int tree_stack[N],top;//分配可用节点的栈
int root;//根节点
void inorder(int u){//拍平u节点及其子树
if(u==0)return;//到达叶子节点
inorder(t[u].ls);//拍平左子树
if(t[u].flag)order[++cnt]=u;//存储,注意是u不是t[u].val,因为t数组顺序不重要,类似邻接表的存储
//中序遍历出的数组储存的节点的val严格不下降
else tree_stack[++top]=u;//如果已经被删除就回收,无需记录
inorder(t[u].rs);//拍平右子树
return;
}
void initnode(int u){//重置节点参数
t[u].ls=t[u].rs=0;
t[u].size=t[u].tot=t[u].flag=1;
return;
}
void update(int u){//更新节点的size、tot的信息
t[u].size=t[t[u].ls].size+t[t[u].rs].size+t[u].flag;
t[u].tot=t[t[u].ls].tot+t[t[u].rs].tot+1;
return;
}
void build(int l,int r,int &u){//把拍平的子树拎起
//需要是&u因为传入的变量需要改变为新的根节点
//这个的改变将会使引用此变量时传入的变量(也就是所在子树的根节点父节点的左/右节点)会更改现在所在子树的根节点,保证其更新
int mid=(l+r)>>1;
u=order[mid];
if(l==r){initnode(u);return;}//如果是叶子节点就返回
if(l<mid)build(l,mid-1,t[u].ls);//重构左子树,传入t[u].ls使其保证左子树重构完成后其值是左子树的根节点所在t[]数组中的位置
if(l==mid)t[u].ls=0;//特判没有左子树
build(mid+1,r,t[u].rs);//重构右子树
update(u);//更新u节点的 size、tot的信息
return;
}
void rebuild(int &u){//重建(拍平+拎起的总函数)
cnt=0;//清空order函数,重新再来
inorder(u);//拍平
if(cnt)build(1,cnt,u);//拎起
else u=0;//特判该子树为空,即没有未被删除的值
}
bool isblance(int u){//判断该子树是否足够平衡
return((double)max(t[t[u].ls].size,t[t[u].rs].size)>=(double)t[u].size*alpha);
//判断左/右子树的所占空间的大小是否大于该子树的总空间大小的alpha倍,即是否平衡
//平衡返回false(即 左/右子树的所占空间的大小小于该子树的总空间大小的alpha倍)
//不平衡返回true
}
void insert(int &u,int x){//递归插入
//注意是&u,传回了插入后的点的位置
if(!u){//u节点为空,即到达可以插入的位置
u=tree_stack[top--];//取出可用空节点
t[u].val=x;
initnode(u);
return;
}
++t[u].size;
++t[u].tot;
insert( (t[u].val>=x?t[u].ls:t[u].rs) ,x);//如果该节点的值大于要插入的值,则插到左子树中。反之,则插到右子树中
if(isblance(u))rebuild(u);//如果不平衡,就重建子树
}
int rankk(int u,int x){//排名,返回x是第几名
if(!u)return 0;
if(x>t[u].val)return t[t[u].ls].size+t[u].flag+rankk(t[u].rs,x);
return rankk(t[u].ls,x);
}
int k_th(int k){//第k大的数是多少
int u=root;
while(u){
if(t[t[u].ls].size+1==k&&t[u].flag)return t[u].val;
else if(t[t[u].ls].size>=k)u=t[u].ls;
else{
k-=t[t[u].ls].size+t[u].flag;
u=t[u].rs;
}
}
return -1;
}
void k_del(int &u,int k){//删除第k大的数
--t[u].size;
if(t[u].flag&&t[t[u].ls].size+1==k){t[u].flag=0;return;}
if(t[u].flag+t[t[u].ls].size>=k)k_del(t[u].ls,k);//在左子树上
else k_del(t[u].rs,k-t[t[u].ls].size-t[u].flag);//在右子树上
}
void del(int x){//删除值为x的数
k_del(root,rankk(root,x)+1);
if(t[root].tot*alpha>=t[root].size)rebuild(root);//如果删太多就重建
return;
}
signed main(){
IOS
for(int i=N-1;i>=1;--i)tree_stack[++top]=i;//初始化,将可用空节点压入栈
int n;
cin>>n;
while(n--){
int opt,x;
cin>>opt>>x;
switch(opt){
case 1:insert(root,x); break;
case 2:del(x); break;
case 3:cout<<rankk(root,x)+1<<endl; break;
case 4:cout<<k_th(x)<<endl; break;
case 5:cout<<k_th(rankk(root,x))<<endl; break;
case 6:cout<<k_th(rankk(root,x+1)+1)<<endl; break;
}
}
return 0;
}