문제
간선이 시간에 따라 추가되고 삭제되며, 중간중간 "현재 연결 요소 수" 또는 "두 정점이 연결됐는가" 를 묻는다. 추가만 있으면 DSU로 충분하지만 삭제가 있으면 DSU는 효율적으로 되돌릴 수 없다. 오프라인이 허용되면 시간축 세그먼트 트리 + 롤백 DSU 로 \(O((n+q)\log q\,\alpha)\) 에 푼다.
핵심 아이디어
각 간선은 어떤 시간 구간 \([t_{add}, t_{del})\) 동안만 존재한다. 이 수명 구간을 시간축 세그먼트 트리에 표준 분해 로 \(O(\log q)\) 개의 노드에 매단다. 그러면 세그 트리를 DFS로 순회하면서:
- 노드에 진입할 때 그 노드에 매달린 간선들을 DSU에 union,
- 리프(=특정 시각)에 도달하면 그 시각의 질의를 현재 DSU 상태로 답하고,
- 노드에서 나올 때 진입 시 했던 union을 롤백.
각 간선은 \(O(\log q)\) 개 노드에 들어가고, 각 노드 방문에서 union/rollback 한 번이므로 전체 \(O((n+q)\log q\,\alpha)\). (\(\alpha\) 는 경로 압축 없는 union-by-rank의 역아커만, 실질 상수.)
롤백 DSU
경로 압축은 되돌리기 어렵다. 그래서 union-by-rank만 쓰고 경로 압축을 포기한다(높이 \(O(\log n)\) 유지). union이 바꾼 부모/랭크를 스택에 기록해 두고, 롤백 시 역순으로 복원한다. 따라서 find 는 \(O(\log n)\), union/rollback은 \(O(\log n)\).
정확성
세그 트리 DFS가 리프 \(t\) 에 도달했을 때 DSU에 들어 있는 간선은 정확히 "수명 구간이 \(t\) 를 포함하는 간선들" 이다. 표준 구간 분해의 성질상, 루트→리프 경로 위 노드들에 매달린 간선의 합집합이 그 시각 활성 간선이기 때문이다.
비교
| 상황 | 도구 |
|---|---|
| 추가만 | DSU |
| 추가+삭제, 오프라인 | 시간 세그 트리 + 롤백 DSU |
| 추가+삭제, 온라인 | 링크-컷 트리 / 유클리드 트릭(어려움) |