import java.util.Random; // Based on Daniel Sleator's implementation http://www.codeforces.com/contest/117/submission/860934 public class LinkCutTreeConnectivity { public static class Node { Node left; Node right; Node parent; boolean revert; // tests whether x is a root of a splay tree boolean isRoot() { return parent == null || (parent.left != this && parent.right != this); } void push() { if (revert) { revert = false; Node t = left; left = right; right = t; if (left != null) left.revert = !left.revert; if (right != null) right.revert = !right.revert; } } } 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()) g.push(); p.push(); x.push(); if (!p.isRoot()) rotate((x == p.left) == (p == g.left) ? p/*zig-zig*/ : x/*zig-zag*/); rotate(x); } x.push(); } // 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 void makeRoot(Node x) { expose(x); x.revert = !x.revert; } public static boolean connected(Node x, Node y) { if (x == y) return true; expose(x); // now x.parent is null expose(y); return x.parent != null; } public static void link(Node x, Node y) { if (connected(x, y)) throw new RuntimeException("error: x and y are already connected"); makeRoot(x); x.parent = y; } public static void cut(Node x, Node y) { makeRoot(x); expose(y); // check that exposed path consists of a single edge (y,x) if (y.right != x || x.left != null || x.right != null) throw new RuntimeException("error: no edge (x,y)"); y.right.parent = null; y.right = null; } // random test public static void main(String[] args) { Random rnd = new Random(1); for (int step = 0; step < 1_000; step++) { int n = rnd.nextInt(50) + 1; boolean[][] g = new boolean[n][n]; Node[] nodes = new Node[n]; for (int i = 0; i < n; i++) nodes[i] = new Node(); for (int query = 0; query < 2_000; query++) { int cmd = rnd.nextInt(10); int u = rnd.nextInt(n); int v = rnd.nextInt(n); Node x = nodes[u]; Node y = nodes[v]; if (cmd == 0) { makeRoot(x); expose(y); if ((y.right == x && x.left == null && x.right == null) != g[u][v]) throw new RuntimeException(); if (y.right == x && x.left == null && x.right == null) { cut(x, y); g[u][v] = g[v][u] = false; } } else if (cmd == 1) { if (connected(x, y) != connected(g, u, v, -1)) throw new RuntimeException(); } else { expose(x); if (connected(x,y) != connected(g, u, v, -1)) throw new RuntimeException(); if (!connected(x,y)) { link(x, y); g[u][v] = g[v][u] = true; } } } } } static boolean connected(boolean[][] g, int u, int v, int p) { if (u == v) return true; for (int i = 0; i < g.length; i++) if (i != p && g[u][i] && connected(g, i, v, u)) return true; return false; } }