import java.util.*;

// Based on Daniel Sleator's implementation http://www.codeforces.com/contest/117/submission/860934
public class LinkCutTreeLca {

	public static class Node {
		Node left;
		Node right;
		Node parent;

		// tests whether x is a root of a splay tree
		boolean isRoot() {
			return parent == null || (parent.left != this && parent.right != this);
		}
	}

	static void connect(Node ch, Node p, Boolean isLeftChild) {
		if (ch != null)
			ch.parent = p;
		if (isLeftChild != null) {
			if (isLeftChild)
				p.left = ch;
			else
				p.right = ch;
		}
	}

	// rotates edge (x, x.parent)
	//        g           g
	//       /           /
	//      p           x
	//     / \   -->   / \
	//    x  p.r     x.l  p
	//   / \             / \
	// x.l x.r         x.r p.r
	static void rotate(Node x) {
		Node p = x.parent;
		Node g = p.parent;
		boolean isRootP = p.isRoot();
		boolean leftChildX = (x == p.left);

		// create 3 edges: (x.r(l),p), (p,x), (x,g)
		connect(leftChildX ? x.right : x.left, p, leftChildX);
		connect(p, x, !leftChildX);
		connect(x, g, !isRootP ? p == g.left : null);
	}

	// brings x to the root, balancing tree
	//
	// zig-zig case
	//        g                                  x
	//       / \               p                / \
	//      p  g.r rot(p)    /   \     rot(x) x.l  p
	//     / \      -->    x       g    -->       / \
	//    x  p.r          / \     / \           x.r  g
	//   / \            x.l x.r p.r g.r             / \
	// x.l x.r                                    p.r g.r
	//
	// zig-zag case
	//      g               g
	//     / \             / \               x
	//    p  g.r rot(x)   x  g.r rot(x)    /   \
	//   / \      -->    / \      -->    p       g
	// p.l  x           p  x.r          / \     / \
	//     / \         / \            p.l x.l x.r g.r
	//   x.l x.r     p.l x.l
	static void splay(Node x) {
		while (!x.isRoot()) {
			Node p = x.parent;
			Node g = p.parent;
			if (!p.isRoot())
				rotate((x == p.left) == (p == g.left) ? p/*zig-zig*/ : x/*zig-zag*/);
			rotate(x);
		}
	}

	// makes node x the root of the virtual tree, and also x becomes the leftmost node in its splay tree
	static Node expose(Node x) {
		Node last = null;
		for (Node y = x; y != null; y = y.parent) {
			splay(y);
			y.left = last;
			last = y;
		}
		splay(x);
		return last;
	}

	public static Node findRoot(Node x) {
		expose(x);
		while (x.right != null)
			x = x.right;
		splay(x);
		return x;
	}

	public static void link(Node x, Node y) {
		if (findRoot(x) == findRoot(y))
			throw new RuntimeException("error: x and y are already connected");
		expose(x);
		if (x.right != null)
			throw new RuntimeException("error: x is not a root node");
		x.parent = y;
	}

	public static void cut(Node x) {
		expose(x);
		if (x.right == null)
			throw new RuntimeException("error: x is a root node");
		x.right.parent = null;
		x.right = null;
	}

	public static Node lca(Node x, Node y) {
		if (findRoot(x) != findRoot(y))
			throw new RuntimeException("error: x and y are not connected");
		expose(x);
		return expose(y);
	}

	// random test
	public static void main(String[] args) {
		Random rnd = new Random(1);
		for (int step = 0; step < 1000; step++) {
			int n = rnd.nextInt(50) + 1;
			int[] p = new int[n];
			Arrays.fill(p, -1);
			Node[] nodes = new Node[n];
			for (int i = 0; i < n; i++)
				nodes[i] = new Node();
			for (int query = 0; query < 10_000; query++) {
				int cmd = rnd.nextInt(10);
				int u = rnd.nextInt(n);
				Node x = nodes[u];
				if (cmd == 0) {
					expose(x);
					if ((x.right != null) != (p[u] != -1))
						throw new RuntimeException();
					if (x.right != null) {
						cut(x);
						p[u] = -1;
					}
				} else if (cmd == 1) {
					int v = rnd.nextInt(n);
					Node y = nodes[v];
					if ((findRoot(x) == findRoot(y)) != (root(p, u) == root(p, v)))
						throw new RuntimeException();
					if (findRoot(x) == findRoot(y)) {
						Node lca = lca(x, y);
						int cur = u;
						Set<Integer> path = new HashSet<>();
						for (; cur != -1; cur = p[cur])
							path.add(cur);
						cur = v;
						for (; cur != -1 && !path.contains(cur); cur = p[cur]);
						if (lca != nodes[cur])
							throw new RuntimeException();
					}
				} else {
					expose(x);
					if ((x.right == null) != (p[u] == -1))
						throw new RuntimeException();
					if (x.right == null) {
						int v = rnd.nextInt(n);
						Node y = nodes[v];
						if ((findRoot(x) != findRoot(y)) != (root(p, u) != root(p, v)))
							throw new RuntimeException();
						if (findRoot(x) != findRoot(y)) {
							link(x, y);
							p[u] = v;
						}
					}
				}
			}
		}
	}

	static int root(int[] p, int u) {
		int root = u;
		while (p[root] != -1)
			root = p[root];
		return root;
	}
}