📅  最后修改于: 2023-12-03 15:42:23.082000             🧑  作者: Mango
本题是门|门模拟 2017的第6题,题目链接:https://loj.ac/problem/1308。
题目描述:
有n个门,每个门都可以用一个二元开关进行开关控制,0表示关闭,1表示打开。门之间的状态有着一定的关系,每个门的状态会受到其他门状态的影响。
给定m个限制条件,每个限制条件形如(Bi, Mi)表示第i个门的状态会受到第Bi个门的状态的影响,其中Mi为一个正整数,表示当第Bi个门被打开时,第i个门的状态就是Mi的状态。
输入格式:
第1行包含2个整数n和m。
接下来m行,每行包含两个整数Bi和Mi。
输出格式:
输出n个整数,表示每个门的最终状态。
本题需要用到并查集来实现门之间状态的合并。每个门可以看作是一个节点,每个节点有一个属于自己的集合,集合的代表为该集合中编号最小的节点。当一个门的状态发生变化时,它所属的集合需要修改,即合并到其他集合中。本题需要支持动态合并,所以我们需要使用带路径压缩和按秩合并的并查集。
对于每个限制条件(Bi,Mi),如果门Bi已经开启,则将门i合并到门Bi所在的集合中,否则标记门i收到门Bi的约束,等到门Bi开启时,再将门i合并到门Bi所在的集合中。
最后,以每个集合的代表作为索引,统计每个集合的最终状态(即所有门状态的异或和),输出答案即可。
class UnionFind:
def __init__(self, n):
self.p = list(range(n))
self.rank = [0] * n
self.cnt = n
def find(self, x):
if x != self.p[x]:
self.p[x] = self.find(self.p[x])
return self.p[x]
def union(self, x, y):
px, py = self.find(x), self.find(y)
if px == py:
return False
if self.rank[px] < self.rank[py]:
px, py = py, px
self.p[py] = px
if self.rank[px] == self.rank[py]:
self.rank[px] += 1
self.cnt -= 1
return True
n, m = map(int, input().split())
uf = UnionFind(n + 1)
broken = [False] * (n + 1)
vis = [-1] * (n + 1)
state = [0] * (n + 1)
for i in range(m):
x, y = map(int, input().split())
state[x] = y
broken[x] = True
for i in range(1, n + 1):
if broken[i]:
vis[i] = i
continue
for j in range(1, n + 1):
if state[j] and uf.find(j) == uf.find(i):
vis[i] = j
break
for i in range(1, n + 1):
if vis[i] == -1:
continue
if broken[vis[i]]:
state[i] ^= state[vis[i]]
else:
state[i] ^= state[vis[i]] ^ state[uf.find(vis[i])]
for i in range(1, n + 1):
if broken[i]:
print(state[i], end=" ")
else:
print(state[uf.find(i)], end=" ")
代码说明:
代码时间复杂度为 $O(m\log^* n)$,空间复杂度为$O(n)$。