The Core Idea: Represent each set as a tree. Each element points to its parent, and the root is the set’s representative.Path Compression: When we find the root of an element, we make all nodes along the path point directly to the root. This flattens the tree, making future queries faster.Union by Rank: When merging two sets, attach the shorter tree under the taller one. This keeps trees balanced.Visual Example:
Copy
Before find(4): After path compression: 1 1 | / | \ 2 2 3 4 | 3 | 4Finding root of 4: 4→3→2→1 (root is 1)After compression: 4→1, 3→1, 2→1 (all point to root)
Why O(α(n))? The inverse Ackermann function α(n) grows incredibly slowly—for any practical n (even n = 10^80), α(n) ≤ 4. So it’s effectively constant time!
Copy
class DSU {public: vector<int> parent, rank_; int components; DSU(int n) : parent(n + 1), rank_(n + 1, 0), components(n) { iota(parent.begin(), parent.end(), 0); // parent[i] = i } int find(int x) { if (parent[x] != x) { parent[x] = find(parent[x]); // Path compression } return parent[x]; } bool unite(int x, int y) { int px = find(x), py = find(y); if (px == py) return false; // Already in same set // Union by rank if (rank_[px] < rank_[py]) swap(px, py); parent[py] = px; if (rank_[px] == rank_[py]) rank_[px]++; components--; return true; } bool connected(int x, int y) { return find(x) == find(y); }};
Problem: Answer “are u and v connected?” after edge additions.
Copy
// Process edges onlinevoid solve() { int n, m, q; cin >> n >> m >> q; DSU dsu(n); // Add initial edges for (int i = 0; i < m; i++) { int u, v; cin >> u >> v; dsu.unite(u, v); } // Answer queries for (int i = 0; i < q; i++) { int type, u, v; cin >> type >> u >> v; if (type == 1) { dsu.unite(u, v); } else { cout << (dsu.connected(u, v) ? "YES" : "NO") << "\n"; } }}
// Problem: For each node, count distinct values in subtreevector<set<int>> nodeSet(n + 1);vector<int> answer(n + 1);void dfs(int u, int p) { nodeSet[u].insert(val[u]); for (int v : adj[u]) { if (v != p) { dfs(v, u); // Merge smaller into larger (small-to-large) if (nodeSet[u].size() < nodeSet[v].size()) { swap(nodeSet[u], nodeSet[v]); } for (int x : nodeSet[v]) { nodeSet[u].insert(x); } } } answer[u] = nodeSet[u].size();}