Structure

Deque history = new ArrayDeque<>(); // {node, oldParent, oldRank}
void union(int x, int y) {
  int px = find(x), py = find(y); // NO path compression
  if (px == py) { history.push(new int[]{-1, -1, -1}); return; }
  if (rank[px] < rank[py]) { int t = px; px = py; py = t; }
  history.push(new int[]{py, parent[py], rank[px]});
  parent[py] = px;
  if (rank[px] == rank[py]) rank[px]++;
}
void rollback() {
  int[] u = history.pop();
  if (u[0] == -1) return;
  parent[u[0]] = u[1];
  rank[parent[u[0]]] = u[2];
}
Advertisement

Complexity

Each op: O(log N) without path compression. Rollback: O(1).

Advertisement

Offline dynamic connectivity

Sweep over time via segment tree over queries. Each edge active in interval — split into O(log Q) segments.

Applications

Offline dynamic MST. Kinetic data structures. Divide-and-conquer over time.