使用无旋treap,在分裂与合并操作过程中动态创建版本之间不同的新点。

// P3835
#include <iostream>
using namespace std;

const int N = 500005;
struct node {
    int l, r;  // 左右儿子
    int val;   // 树的权值
    int rnd;   // 堆的随机值
    int size;  // 子树大小
} tr[N * 50];
int root[N], idx;

void newnode(int& x, int v) {
    x = ++idx;
    tr[x].val = v;
    tr[x].rnd = rand();
    tr[x].size = 1;
}
void pushup(int p) {
    tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + 1;
}

// 分裂,动态开点复制路径上的节点,作为新版本节点。
void split(int p, int v, int& x, int& y) {
    if (!p) {
        x = y = 0;
        return;
    }
    if (tr[p].val <= v) {
        x = ++idx;
        tr[x] = tr[p];
        split(tr[x].r, v, tr[x].r, y);
        pushup(x);
    } else {
        y = ++idx;
        tr[y] = tr[p];
        split(tr[y].l, v, x, tr[y].l);
        pushup(y);
    }
}
// int merge(int x,int y){
//   if(!x||!y) return x+y;
//   int p = ++idx; // 合并无需新节点
//   if(tr[x].rnd<tr[y].rnd){
//     tr[p]=tr[x];
//     tr[p].r=merge(tr[p].r,y);
//     pushup(p); return p;
//   }
//   else{
//     tr[p]=tr[y];
//     tr[p].l=merge(x,tr[p].l);
//     pushup(p); return p;
//   }
// }
int merge(int x, int y) {
    if (!x || !y)
        return x + y;
    if (tr[x].rnd < tr[y].rnd) {
        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;
    }
}
void insert(int& root, int v) {
    int x, y, z;
    split(root, v, x, y);
    newnode(z, v);
    root = merge(merge(x, z), y);
}
void del(int& root, int v) {
    int x, y, z;
    split(root, v, x, z);
    split(x, v - 1, x, y);
    y = merge(tr[y].l, tr[y].r);
    root = merge(merge(x, y), z);
}
int getrank(int& root, int v) {
    int x, y;
    split(root, v - 1, x, y);
    int ans = tr[x].size + 1;
    root = merge(x, y);
    return ans;
}
int getval(int root, int v) {
    if (v == tr[tr[root].l].size + 1)
        return tr[root].val;
    else if (v <= tr[tr[root].l].size)
        return getval(tr[root].l, v);
    else
        return getval(tr[root].r, v - tr[tr[root].l].size - 1);
}
int getpre(int& root, int v) {
    int x, y, s, ans;
    split(root, v - 1, x, y);
    if (!x)
        return -2147483647;
    s = tr[x].size;
    ans = getval(x, s);
    root = merge(x, y);
    return ans;
}
int getnxt(int& root, int v) {
    int x, y, ans;
    split(root, v, x, y);
    if (!y)
        return 2147483647;
    else
        ans = getval(y, 1);
    root = merge(x, y);
    return ans;
}
int main() {
    int n, ver, op, v;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d%d%d", &ver, &op, &v);
        root[i] = root[ver];
        if (op == 1)
            insert(root[i], v);
        else if (op == 2)
            del(root[i], v);
        else if (op == 3)
            printf("%d\\n", getrank(root[i], v));
        else if (op == 4)
            printf("%d\\n", getval(root[i], v));
        else if (op == 5)
            printf("%d\\n", getpre(root[i], v));
        else
            printf("%d\\n", getnxt(root[i], v));
    }
    return 0;
}