M18250: 冰阔落 I
disjoint set, http://cs101.openjudge.cn/pctbook/M18250/
老王喜欢喝冰阔落。
初始时刻,桌面上有n杯阔落,编号为1到n。老王总想把其中一杯阔落倒到另一杯中,这样他一次性就能喝很多很多阔落,假设杯子的容量是足够大的。
有m 次操作,每次操作包含两个整数x与y。
若原始编号为x 的阔落与原始编号为y的阔落已经在同一杯,请输出"Yes";否则,我们将原始编号为y 所在杯子的所有阔落,倒往原始编号为x 所在的杯子,并输出"No"。
最后,老王想知道哪些杯子有冰阔落。
输入
有多组测试数据,少于 5 组。 每组测试数据,第一行两个整数 n, m (n, m<=50000)。接下来 m 行,每行两个整数 x, y (1<=x, y<=n)。
输出
每组测试数据,前 m 行输出 "Yes" 或者 "No"。 第 m+1 行输出一个整数,表示有阔落的杯子数量。 第 m+2 行有若干个整数,从小到大输出这些杯子的编号。
样例输入
3 2
1 2
2 1
4 2
1 2
4 3样例输出
No
Yes
2
1 3
No
No
2
1 4来源
Gong linyuan, Xu Yewen
并查集的普遍问题:各节点的根没有及时更新到最深的根,各个节点的更新不是及时的。这道题里只有最深层的根是有效的。
在并查集中,当一个节点的根节点更新为另一个节点时,如果该节点之后再次被更新为另一个节点的子节点,就会导致路径压缩未完全实现的情况。这可能会使得某些节点的根节点不是最深的根节点,而是更新过程中的某个中间节点。
例如:A路径压缩更新到B,但是B后来又更新为C了,导致A的根结点不是C。
python
def find(x):
if parent[x] != x:
parent[x] = find(parent[x])
return parent[x]
def union(x, y):
root_x = find(x)
root_y = find(y)
if root_x != root_y:
parent[root_y] = root_x
while True:
try:
n, m = map(int, input().split())
parent = list(range(n + 1))
for _ in range(m):
a, b = map(int, input().split())
if find(a) == find(b):
print('Yes')
else:
print('No')
union(a, b)
unique_parents = set(find(x) for x in range(1, n + 1)) # 获取不同集合的根节点
ans = sorted(unique_parents) # 输出有冰阔落的杯子编号
print(len(ans))
print(*ans)
except EOFError:
break