Road Construction II - Union-Find with Component Tracking
Problem Overview
| Aspect | Details |
|---|---|
| Problem | Track connectivity after each road: number of components and largest component size |
| Input | n cities, m roads added one by one |
| Output | After each road: (component_count, max_component_size) |
| Constraints | 1 <= n, m <= 10^5 |
| Key Technique | Union-Find with size tracking |
Problem Description
There are n cities and m roads to be built. After each road is constructed, report:
- The number of connected components
- The size of the largest component
Example:
Input:
5 3
1 2
1 3
4 5
Output:
4 2
3 3
2 3
Explanation:
After road 1-2: 4 components {1,2}, {3}, {4}, {5}, largest = 2
After road 1-3: 3 components {1,2,3}, {4}, {5}, largest = 3
After road 4-5: 2 components {1,2,3}, {4,5}, largest = 3
Algorithm: Union-Find with Size Tracking
Key Insight: Standard Union-Find tracks connectivity, but we also need to track component sizes and maintain the maximum.
Data Structure:
parent[i]: parent of node i (root points to itself)size[i]: size of component rooted at icomponents: current number of componentsmax_size: size of largest component
Operations:
- Find(x): Find root with path compression
- Union(x, y): Merge components, update sizes, track max
Initial: n=5, components=5, max_size=1
[1] [2] [3] [4] [5]
After union(1,2):
[1,2] [3] [4] [5]
components=4, max_size=2
After union(1,3):
[1,2,3] [4] [5]
components=3, max_size=3
After union(4,5):
[1,2,3] [4,5]
components=2, max_size=3
Implementation
Python Solution
import sys
from sys import stdin
def solve():
input = stdin.readline
n, m = map(int, input().split())
parent = list(range(n + 1))
size = [1] * (n + 1)
components = n
max_size = 1
def find(x):
if parent[x] != x:
parent[x] = find(parent[x])
return parent[x]
def union(a, b):
nonlocal components, max_size
ra, rb = find(a), find(b)
if ra == rb:
return # Already connected
# Union by size
if size[ra] < size[rb]:
ra, rb = rb, ra
parent[rb] = ra
size[ra] += size[rb]
components -= 1
max_size = max(max_size, size[ra])
result = []
for _ in range(m):
a, b = map(int, input().split())
union(a, b)
result.append(f"{components} {max_size}")
print('\n'.join(result))
solve()
Complexity Analysis
| Operation | Time | Space |
|---|---|---|
| Initialization | O(n) | O(n) |
| Each Query | O(alpha(n)) | - |
| Total | O(n + m * alpha(n)) | O(n) |
Where alpha(n) is the inverse Ackermann function, effectively O(1).
Common Mistakes
- Forgetting path compression: Without it, worst-case O(n) per find
- Not tracking max_size correctly: Must update when merging, not when connecting same component
- Off-by-one in indexing: Cities are 1-indexed
- Integer overflow: Use
long longfor component sizes if they can exceed 2^31 - Printing inside loop: Slow in Python; collect results and print once
Key Takeaways
- Union-Find naturally tracks connected components
- Adding size tracking enables finding largest component
- Path compression + union by size gives near O(1) operations
- Maintain max incrementally rather than recomputing each query