#include <iostream>
using namespace std;

// 定义最大节点数量,限制树的规模
const int N = 100005;

// 定义Treap树的节点结构
struct node {
    int l, r;       // 左右儿子的索引(0表示空节点)
    int val;        // 节点存储的值(用于维护二叉搜索树BST性质:左子树<=根<=右子树)
    int rnd;        // 随机值(用于维护堆性质:父节点rnd <= 子节点rnd,保证树平衡)
    int size;       // 以当前节点为根的子树中节点总数(用于快速计算排名等)
} tr[N];            // 用数组存储所有节点,tr[i]表示索引为i的节点

int root;           // 根节点的索引(初始为0,代表空树)
int idx;            // 节点计数器,用于分配新节点的唯一索引(从1开始递增)

// 创建新节点:为值v分配一个新节点,并用x接收新节点的索引
void newnode(int &x, int v) {
    x = ++idx;          // 分配新索引(idx自增后赋值给x)
    tr[x].val = v;      // 设置节点的值为v
    tr[x].rnd = rand(); // 随机生成rnd值(用于后续维护堆性质)
    tr[x].size = 1;     // 新节点初始只有自身,子树大小为1
    // 新节点的左右儿子默认为0(空),因全局变量初始值为0
}

// 更新节点p的子树大小(当左右子树变化时必须调用)
void pushup(int p) { 
    // 子树大小 = 左子树大小 + 右子树大小 + 1(当前节点)
    tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + 1;
}

// 分裂操作:将以p为根的树按值v分裂为两棵树
// x为左树(所有节点值<=v),y为右树(所有节点值>v)
void split(int p, int v, int &x, int &y) {
    if (!p) {         // 若当前节点为空(p=0)
        x = y = 0;    // 分裂后的两棵树也为空
        return;
    }
    
    if (tr[p].val <= v) {  // 当前节点值<=v,应属于左树x
        x = p;             // 左树x的根设为当前节点p
        // 递归分裂p的右子树:右子树中<=v的部分成为p的新右子树(仍属x),>v的部分成为y的一部分
        split(tr[x].r, v, tr[x].r, y);
        pushup(x);         // 因右子树变化,更新x的子树大小
    } else {               // 当前节点值>v,应属于右树y
        y = p;             // 右树y的根设为当前节点p
        // 递归分裂p的左子树:左子树中<=v的部分成为x的一部分,>v的部分成为p的新左子树(仍属y)
        split(tr[y].l, v, x, tr[y].l);
        pushup(y);         // 因左子树变化,更新y的子树大小
    }
}

// 合并操作:将两棵树x和y合并为一棵(前提:x中所有节点值<=y中所有节点值)
// 返回合并后的根节点索引
int merge(int x, int y) {
    if (!x || !y) return x + y;  // 若有一棵树为空,直接返回另一棵(0+x=x,x+0=x)
    
    // 比较rnd值,维持堆性质(rnd小的节点作为父节点)
    if (tr[x].rnd < tr[y].rnd) {
        // x的rnd更小,x作为根,将x的右子树与y合并后作为x的新右子树
        tr[x].r = merge(tr[x].r, y);
        pushup(x);  // 因右子树变化,更新x的子树大小
        return x;   // 返回新根x
    } else {
        // y的rnd更小,y作为根,将x与y的左子树合并后作为y的新左子树
        tr[y].l = merge(x, tr[y].l);
        pushup(y);  // 因左子树变化,更新y的子树大小
        return y;   // 返回新根y
    }
}

// 插入操作:向树中插入值v
void insert(int v) {
    int x, y, z;               // 临时变量用于分裂和合并
    split(root, v, x, y);      // 分裂根节点:x含<=v的节点,y含>v的节点
    newnode(z, v);             // 创建值为v的新节点z
    // 先合并x和z(保证x中节点<=z),再合并结果与y(保证合并后节点<=y),得到新根
    root = merge(merge(x, z), y);
}

// 删除操作:从树中删除值v
void del(int v) {
    int x, y, z;               // 临时变量
    split(root, v, x, z);      // 第一次分裂:x含<=v,z含>v
    split(x, v-1, x, y);       // 第二次分裂:x含<=v-1,y含=v的节点(精确分离要删除的节点)
    // 合并y的左右子树(相当于删除y中所有值为v的节点)
    y = merge(tr[y].l, tr[y].r);
    // 重新合并所有部分,恢复树结构
    root = merge(merge(x, y), z);
}

// 查询值v的排名(即有多少个数小于v,结果+1)
int getrank(int v) {
    int x, y;
    split(root, v-1, x, y);  // 分裂出所有<=v-1的节点到x(这些节点都小于v)
    int ans = tr[x].size + 1;  // x的大小是小于v的数量,+1即为v的排名
    root = merge(x, y);      // 合并回原树(不改变树结构)
    return ans;
}

// 查询排名为v的值(第v小的数)
int getval(int root, int v) {
    // 左子树大小+1等于v,说明当前节点就是第v小
    if (v == tr[tr[root].l].size + 1)
        return tr[root].val;
    // 若v小于左子树大小,说明第v小在左子树中
    else if (v <= tr[tr[root].l].size)
        return getval(tr[root].l, v);
    // 否则在右子树中,v需减去左子树大小和当前节点(1)
    else 
        return getval(tr[root].r, v - tr[tr[root].l].size - 1);
}

// 查询v的前驱(小于v的最大数)
int getpre(int v) {
    int x, y, s, ans;
    split(root, v-1, x, y);  // 分裂出所有<=v-1的节点到x(这些节点都小于v)
    s = tr[x].size;          // x的大小是<=v-1的节点数量
    ans = getval(x, s);      // 取x中最大的数(第s小,即最后一个元素)
    root = merge(x, y);      // 合并回原树
    return ans;
}

// 查询v的后继(大于v的最小数)
int getnxt(int v) {
    int x, y, ans;
    split(root, v, x, y);    // 分裂出所有<=v的节点到x,y中节点都>v
    ans = getval(y, 1);      // 取y中最小的数(第1小,即第一个元素)
    root = merge(x, y);      // 合并回原树
    return ans;
}

int main() {
    int n, op, v;             // n:操作总数;op:操作类型(1-6);v:操作参数
    scanf("%d", &n);          // 读取操作数量
    for (int i = 1; i <= n; ++i) {  // 循环处理每个操作
        scanf("%d%d", &op, &v);     // 读取操作类型和参数
        if (op == 1) insert(v);     // 1:插入值v
        else if (op == 2) del(v);   // 2:删除值v
        else if (op == 3) printf("%d\n", getrank(v));  // 3:查询v的排名
        else if (op == 4) printf("%d\n", getval(root, v));  // 4:查询第v小的值
        else if (op == 5) printf("%d\n", getpre(v));  // 5:查询v的前驱
        else printf("%d\n", getnxt(v));  // 6:查询v的后继
    }
    return 0;
}