//无旋treap真滴nb。。基于spilt和merge两个函数可以做很多事情。。
之前有一道模板题基于那道题多了版本更新操作,思路和主席树的思路类似,只新建插入或删除的那条链上的点,其他的点参照之前的版本。
然后就没有然后了。。

1.因为要复制一些节点的信息,把节点的数据用结构体存起来比较好写。
2.空间开40倍。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5+5;
typedef long long ll;
const ll mod = 1e9+7;
int Case = 1;
int n, m;
struct Treap{
    struct node{
        int ch[2], dat;
        int val, size;
    }tr[maxn*40];
    int root[maxn];
    int tot, cnt;
    void pushup(int rt) {
        tr[rt].size = tr[tr[rt].ch[0]].size + 1 + tr[tr[rt].ch[1]].size;
    }
    int copy(int rt) {
        tot++;
        tr[tot] = tr[rt];
        return tot;
    }
    int newnode(int x) {
        tot++;
        tr[tot].size = 1;
        tr[tot].dat = rand();
        tr[tot].val = x;
        return tot;
    }
    void spilt(int rt, int k, int &x, int &y) {
        if(!rt)  x = y = 0;
        else{
            if(tr[tr[rt].ch[0]].size >= k) {
                y = copy(rt);
                spilt(tr[y].ch[0], k, x, tr[y].ch[0]);
                pushup(y);
            }
            else{
                x = copy(rt);
                spilt(tr[x].ch[1], k-tr[tr[x].ch[0]].size-1, tr[x].ch[1], y);
                pushup(x);
            }
            
        }
    }
    int merge(int A, int B) {
        if(!A || !B) return A + B;
        if(tr[A].dat < tr[B].dat) {
            int res = copy(A);
            tr[res].ch[1] = merge(tr[res].ch[1], B);
            pushup(res);
            return res;
        }
        else {
            int res = copy(B);
            tr[res].ch[0] = merge(A, tr[res].ch[0]);
            pushup(res);
            return res;
        }
    }
    int insert(int rt, int k, int x) {
        int node = newnode(x);
        int a, b;
        spilt(rt, k-1, a, b);
        // puts("!!!");
        // dfs(a);puts("");
        // dfs(b);puts("");
        // puts("!!!");
        return merge(merge(a, node), b);
    }
    int del(int rt, int k) {
        int a, b, c;
        spilt(rt, k-1, a, b);
        spilt(b, 1, b, c);
        return merge(a, c);
    }
    int query(int rt, int k) {
        if(!rt) return -1;
        if(k <= tr[tr[rt].ch[0]].size) return query(tr[rt].ch[0], k);
        else if(k == tr[tr[rt].ch[0]].size + 1) return tr[rt].val;
        else return query(tr[rt].ch[1], k-1-tr[tr[rt].ch[0]].size);
    }
    void dfs(int rt) {
        if(!rt) return;
        dfs(tr[rt].ch[0]);
        printf("%d ", tr[rt].val);
        dfs(tr[rt].ch[1]);
    }
    void add() {
        cnt++;
    }
}treap;
void solve() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        int op;scanf("%d", &op);
        if(op == 1) {
            int t, k, val;
            scanf("%d%d%d", &t, &k, &val);
            treap.add();
            treap.root[treap.cnt] = treap.insert(treap.root[t], k, val);
            //treap.dfs(treap.root[treap.cnt]); 
        }
        else if(op == 2) {
            int k, t;scanf("%d%d", &t, &k);
            treap.add();
            treap.root[treap.cnt] = treap.del(treap.root[t], k);
            //
        }
        else if(op == 3) {
            int t, k;scanf("%d%d", &t, &k);
            printf("%d\n", treap.query(treap.root[t], k));
            
            //puts("");
        }
        //printf("%d\n", treap.size[treap.root[3]]);
        //treap.dfs(treap.root[treap.cnt]);
        //puts("");
    }
    return;
}
int main() {
    //g++ -std=c++11 -o2 1.cpp -o f && ./f < in.txt
    //ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt","w",stdout);
#endif
while(Case--) {
    solve();
    }
return 0;
}