#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n, m;
const int MAXN = 1000005;
int tot, v[MAXN], l[MAXN], r[MAXN], d[MAXN];
int gc[MAXN]; //gc[i]表示编号为i的工厂所在的左偏树是哪个x
class Leftist_Tree {
int _Merge(int x, int y) {
if(!x || !y)
return x + y;
if(v[x] < v[y])
swap(x, y);
r[x] = _Merge(r[x], y);
if(d[l[x]] < d[r[x]])
swap(l[x], r[x]);
d[x] = d[r[x]] + 1;
return x;
}
int Build(int val = -1) {
tot++;
l[tot] = r[tot] = d[tot] = 0;
v[tot] = val;
return tot;
}
public:
void Init(int n) {
tot = n; //当前树的上界到了哪个
for(int i = 1; i <= n; i++) {
l[i] = r[i] = d[i] = 0;
v[i] = -1;
}
l[0] = r[0] = -1;
d[0] = 0;
v[0] = -1;
}
void Push(int x, int val) {
int gcx = gc[x];
int rt = Build(val);
rt = _Merge(rt, gcx);
gc[x] = rt;
}
int Pop(int x) {
//把x工厂的左偏树的顶端弹出,没有顶端弹出-1
int gcx = gc[x];
if(v[gcx] == -1)
return -1;
int res = v[gcx];
gc[x] = _Merge(l[gcx], r[gcx]);
return res;
}
void Merge(int x, int y) {
//把编号x与编号y的工厂合并到x工厂
int gcx = gc[x];
int gcy = gc[y];
gc[x] = _Merge(gcx, gcy);
gc[y] = Build();
}
} lt;
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int n, m;
scanf("%d%d", &n, &m);
lt.Init(n);
for(int i = 1; i <= m; i++) {
int ins;
scanf("%d", &ins);
switch(ins) {
case 1: {
int x, v;
scanf("%d%d", &x, &v);
lt.Push(x, v);
break;
}
case 2: {
int x, y;
scanf("%d%d", &x, &y);
lt.Merge(x, y);
break;
}
case 3: {
int x;
scanf("%d", &x);
printf("%d
", lt.Pop(x));
break;
}
}
}
}