You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

188 lines
4.6 KiB
Java

5 years ago
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;
}
}