Lam NguyenLibrary

Library · Graphs · union-find

DSU with Rollback

#pragma once
#include <bits/stdc++.h>
using namespace std;

struct DSURollback {
    int n;
    vector<int> parent, sz, history;
    DSURollback(int _n) : n(_n) {
        sz.assign(n, 1);
        parent.resize(n);
        for (int i = 0; i < n; i++) parent[i] = i;
    }
    int find(int v) {
        if (v == parent[v]) return v;
        // no path compression
        return find(parent[v]);
    }
    bool unite(int a, int b) {
        a = find(a);
        b = find(b);
        if (a == b) return false;

        if (sz[a] < sz[b]) swap(a, b);
        
        parent[b] = a;
        sz[a] += sz[b];

        // remember who became child, extend to History struct as needed
        history.push_back(b); 
        return true;
    }
    void rollback() {
        if (history.empty()) return;

        int b = history.back();
        history.pop_back();

        int a = parent[b];

        sz[a] -= sz[b];
        parent[b] = b;
    }
};
class DSURollback:
    def __init__(self, n: int) -> None:
        self.par = list(range(n))
        self.sz = [1] * n
        self.history = [] # who became a child, one entry per successful unite

    def find(self, v: int) -> int:
        while v != self.par[v]: # no path compression: it would make undo impossible
            v = self.par[v]
        return v

    def unite(self, a: int, b: int) -> bool:
        a, b = self.find(a), self.find(b)
        if a == b: return False
        if self.sz[a] < self.sz[b]: # union by size keeps find O(log n)
            a, b = b, a
        self.par[b] = a
        self.sz[a] += self.sz[b]
        self.history.append(b)
        return True

    # undo the last successful unite
    def rollback(self) -> None:
        if not self.history: return
        b = self.history.pop()
        a = self.par[b]
        self.sz[a] -= self.sz[b]
        self.par[b] = b