F - 友達の輪 / Circle of Friends Editorial by admin
DeepSeek V3Overview
This problem requires efficiently managing groups of students, answering queries about group merging and group sizes quickly.
Analysis
In a naive approach, updating the membership of all students every time a group merge is performed would take \(O(N)\) time per merge, resulting in \(O(NQ)\) overall. Under the constraints \(N, Q \leq 2 \times 10^5\), this would not run within the time limit. Therefore, an efficient data structure for managing groups is needed.
Algorithm
This problem can be solved efficiently using the Union-Find (Disjoint Set Union, DSU) data structure. Union-Find supports the following two operations efficiently: - find(x): Find the representative (root) of the group that element \(x\) belongs to - union(a, b): Merge the groups that elements \(a\) and \(b\) belong to
Additionally, we maintain an array size to track the size (number of elements) of each group. This allows us to answer group size queries simply by returning the size stored at the root.
The implementation uses the following two optimization techniques:
1. Path Compression: During the find operation, connect each visited node directly to the root, keeping the tree height small
2. Union by size: During merging, attach the smaller tree to the larger tree, keeping the tree height small
With these optimizations, each operation runs in nearly constant time \(O(\alpha(N))\) (\(\alpha\) is the inverse Ackermann function).
Complexity
- Time complexity: \(O(Q \cdot \alpha(N))\)
- Space complexity: \(O(N)\)
Implementation Notes
Implementing Union-Find using a class helps keep the code organized
Combining path compression and union by size ensures efficiency
Prepare an array
sizeto manage group sizes, storing the size at the root nodeTo speed up input processing, use
sys.stdin.readfor bulk readingAlso accumulate output in a list and print it all at once at the end for faster output
Source Code
class UnionFind:
def __init__(self, n):
self.parent = list(range(n+1))
self.size = [1] * (n+1)
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, a, b):
root_a = self.find(a)
root_b = self.find(b)
if root_a == root_b:
return
if self.size[root_a] < self.size[root_b]:
root_a, root_b = root_b, root_a
self.parent[root_b] = root_a
self.size[root_a] += self.size[root_b]
def main():
import sys
input = sys.stdin.read
data = input().split()
n = int(data[0])
q = int(data[1])
uf = UnionFind(n)
index = 2
output_lines = []
for _ in range(q):
t = data[index]; index += 1
if t == '1':
a = int(data[index]); b = int(data[index+1]); index += 2
uf.union(a, b)
else:
x = int(data[index]); index += 1
root = uf.find(x)
output_lines.append(str(uf.size[root]))
print("\n".join(output_lines))
if __name__ == "__main__":
main()
This editorial was generated by deepseekv3.
posted:
last update: