#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
std::mt19937 rnd(std::random_device{}());
struct
{
int l, r; // 左右孩子
int val; // BST 的权值
int rnd; // 堆的随机值
int size; // 树的大小
} tr[N];
int root = 0, cnt = 0; // 根节点, 最新节点的下标
// 创建一个新节点
int newnode(int v)
{
tr[++cnt].val = v;
tr[cnt].rnd = rnd();//赋一个随机值
tr[cnt].size = 1;
return cnt;
}
// 更新树的大小
void pushup(int p) { tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + 1; }
// 按值 v 进行分裂, 左子树 <=v, 右子树 > v
// x, y 是分裂后的两颗子树的根, 执行完成后 x.val <= v < y.val
void split(int p, int v, int &x, int &y) {
if (!p) { // 空树
x = y = 0;
return;
}
if (tr[p].val <= v) {
x = p;
split(tr[p].r, v, tr[p].r, y);
// 递归分裂 p 的右子树
// 左子树的根 x 已确定, y 需要继续向下带
// 同时, p 的右节点需要指向下一层指向的左子树的根 x
}
else { // 递归分裂 p 的左子树
y = p;
split(tr[p].l, v, x, tr[p].l);
}
pushup(p); //向上更新每个子树的大小
}
// 按堆的随机值合并两颗子树, 返回合并后的根
// 要求 x 树所有节点的 val 要 <= y 树所有节点的 val 值
int merge(int x, int y)
{
if (!x || !y) return x + y; // 存在空树的情况
if (tr[x].rnd < tr[y].rnd) {
// 应把 x 放上面, 先递归合并 x 的右子树 r 和 y 得到新树 z
// 因为 x 权值更小, 所以把 z 作为 x 的右孩子
tr[x].r = merge(tr[x].r, y);
pushup(x);
return x;
}
else {
tr[y].l = merge(x, tr[y].l);
pushup(y);
return y;
}
}
int size() { return tr[root].size; }
// 插入一个值
void insert(int v) { // 按 v 分裂, 找到插入点 x <=v < y
int x, y;
split(root, v, x, y); // 新建节点
int z = newnode(v);
root = merge(merge(x, z), y); // 依次合并 x, z 和 y (权值 val 也满足如此顺序)
}
// 删除一个值
void del(int v)
{
int x, y, z;
split(root, v, x, z); // 先找到 v 的分割点 => x <= v < z
split(x, v - 1, x, y); // 再按 v-1 分裂 x 树 => x <= v-1 < y <= v
y = merge(tr[y].l, tr[y].r); // y 就是值等于 v 的节点, 删除它.
//如果找不到 v, y 就是空树, 其左右孩子都是 0, 不影响
root = merge(merge(x, y), z);// 合并x, y, z
}
// 查找排名, 满足 < v 的个数 + 1
int rk(int v)
{
int x, y;
split(root, v - 1, x, y);
int ans = tr[x].size + 1;
root = merge(x, y);
return ans;
}
// 第 k 小
int kth(int p, int k)
{
int lsz = tr[tr[p].l].size; //左子树大小
if (k == lsz + 1) return tr[p].val; //恰等于,直接返回
if (k <= lsz) return kth(tr[p].l, k); //大了,去左子树找
return kth(tr[p].r, k - lsz - 1); //去右子树找
}
// 前驱
int pre(int v)
{
int x, y;
split(root, v - 1, x, y);
int ans = kth(x, tr[x].size);
root = merge(x, y);
return ans;
}
// 后继
int nxt(int v)
{
int x, y;
split(root, v, x, y);
int ans = kth(y, 1);
root = merge(x, y);
return ans;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin >> n;
int ans = 0;
while (n--)
{
int opt, x;
cin >> opt >> x;
switch (opt)
{
case 1:
insert(x);
break;
case 2:
del(x);
break;
case 3:
ans = rk(x);
break;
case 4:
ans = kth(root, x);
break;
case 5:
ans = pre(x);
break;
case 6:
ans = nxt(x);
break;
}
if(opt >= 3) cout << ans << '\n';
}
return 0;
}