Union-Find Code II

Union-Find Code II

Start with n elements, apply the given union operations, then answer connectivity queries: for each query a,b, is a in the same group as b?

connectivity(n, unions, queries)

Input format: n | unions | queries, both as a,b pairs. Output the booleans space-separated.

Examples

n=5, unions (0,1)(1,2), queries (0,2)(0,3)   // → [true, false]

Walkthrough

Thinking it through

Two phases glued together: build a picture of which elements end up grouped after all unions, then answer "are these two in the same group?" for each query. Union-find is designed for exactly this split — cheap merges, cheap membership questions.

Why a fresh check per query, not a fresh traversal

A naive approach builds an explicit graph and runs BFS/DFS per query to check connectivity — repeating work, since every query re-explores potentially large chunks of the graph. Union-find maintains a compressed "which root does each element belong to" incrementally. Once built, "are a and b connected" reduces to: do they share a root?

The two-phase approach

  1. Build phase. Every element starts as its own group. For each union, find both roots (compressing paths along the way) and link them if different. After all unions, the structure reflects every merge, direct or transitive — a unioned with b, b with c, means a's root equals c's root even though a and c were never unioned directly.
  2. Query phase. For each query, find both roots and compare. Same root means connected — true; otherwise false. No traversal at query time, just two lookups and a comparison.

Why finding the root answers connectivity correctly

The invariant: two elements share a group exactly when following their parent pointers leads to the same root. Every merge preserves this by construction, and path compression never changes which root a chain leads to — only speeds up future lookups. So root equality accurately reflects "connected, directly or transitively, by the unions." Processing all unions first, then all queries, is correct and efficient: each find costs near-constant time thanks to path compression, so the whole process runs in close to linear time.

main.py
Console
Press Run to execute your code, or Submit to test and complete this problem.