晚上好🌙🌙🌙
本答案参考通义千问
连通分量(Connected Component) 是图论中的一个基本概念。简单来说,在一个无向图中,如果两个顶点之间可以通过一系列边连接起来,那么这两个顶点就属于同一个连通分量。
举个例子:
- 如果你有一张图,其中有些点互相连着,而另一些点不连着,那么每个“独立的块”就是一个连通分量。
- 例如,如果图中有3个互不相连的点,那么就有3个连通分量;如果有两个点相连,另一个点不连,那么就是2个连通分量。
题目解析
题目给出的是一个特殊的图结构,其构造规则如下:
如果两个整数 X 和 Y 满足 X & Y = 0(按位与),则它们之间有一条无向边。
也就是说,只要两个数在二进制下没有任何一位同时为1,它们之间就会有边。
我们的任务是:给定一组数,计算这个图中有多少个连通分量。
解题思路
1. 理解图的构造方式
我们不是直接构造整个图,而是通过判断两个数是否满足 X & Y == 0 来判断是否有边。
但是,如果我们直接对所有数两两比较,时间复杂度会很高(比如 m=2^22 时,m^2 就是 2^44,显然不行)。
所以我们要找一个更高效的方式。
2. 观察性质:连通分量与二进制位有关
注意到:两个数 X 和 Y 能连通的条件是 它们的二进制表示没有重叠的1。
我们可以把这个问题转化为一个集合问题:找出所有可以相互连接的数,形成一个连通分量。
3. 使用并查集(Union-Find)算法
我们可以使用并查集来处理这个问题:
- 初始化每个数为一个独立的集合。
- 对于每一对数 (X, Y),如果
X & Y == 0,就把它们合并到一个集合中。 - 最后统计有多少个不同的集合,就是连通分量的数量。
4. 优化方法(关键)
由于 n 的范围是 [0, 22],即最大是 2^22 ≈ 4 million。对于 m ≤ 2^n,我们可以考虑以下优化:
- 将所有的数转换成二进制形式,然后根据它们的二进制位来判断是否可以连通。
- 但这样还是需要两两比较,时间复杂度太高。
5. 更高效的观察
我们可以从位的角度来看:
- 一个数 X 可以和那些二进制位与 X 的二进制位没有交集的数相连。
- 所以,我们可以将数按照它们的二进制位进行分类,看看哪些数能组成一个连通分量。
举个例子帮助理解
输入样例:
2 3
1 2 3
这表示 n=2,也就是所有数都在 [0, 3] 范围内,共有 3 个数:1, 2, 3。
我们来看看它们之间的关系:
- 1 (01) 和 2 (10):1&2=0 → 有边
- 1 (01) 和 3 (11):1&3=1 → 没有边
- 2 (10) 和 3 (11):2&3=2 → 没有边
所以图中只有 1 和 2 相连,3 单独存在。
因此,连通分量数量是 2。
正确的解法步骤(详细)
✅ 步骤一:读取输入
- 读取 n 和 m。
- 读取 m 个整数 a1, a2, ..., am。
✅ 步骤二:初始化并查集
✅ 步骤三:遍历所有数对
- 对于每对 i < j:
- 如果
a[i] & a[j] == 0,说明它们之间有边,将它们合并。
✅ 步骤四:统计连通分量数量
- 遍历所有数,统计有多少个不同的根节点(即不同的集合)。
示例代码(Python)
def find(parent, x):
if parent[x] != x:
parent[x] = find(parent, parent[x])
return parent[x]
def union(parent, rank, x, y):
root_x = find(parent, x)
root_y = find(parent, y)
if root_x != root_y:
if rank[root_x] > rank[root_y]:
parent[root_y] = root_x
else:
parent[root_x] = root_y
if rank[root_x] == rank[root_y]:
rank[root_y] += 1
def count_components(n, m, nums):
parent = {}
rank = {}
for num in nums:
parent[num] = num
rank[num] = 0
for i in range(m):
for j in range(i + 1, m):
if (nums[i] & nums[j]) == 0:
union(parent, rank, nums[i], nums[j])
roots = set()
for num in nums:
roots.add(find(parent, num))
return len(roots)
# 测试样例
import sys
input = sys.stdin.read
data = input().split()
idx = 0
while idx < len(data):
n = int(data[idx])
m = int(data[idx + 1])
idx += 2
nums = list(map(int, data[idx:idx + m]))
idx += m
print(count_components(n, m, nums))
重点总结
- 连通分量:图中互相可达的点构成一个连通分量。
- 关键条件:X & Y == 0 表示 X 和 Y 可以连通。
- 解决方法:使用并查集来合并符合条件的点。
- 时间复杂度:O(m²)(在 m 较小的情况下可行)。
- 优化建议:对于大范围数据,可以尝试基于二进制位的优化。
如果你是初中生,没关系!这个题目虽然看起来难,但理解了基本概念之后,就可以一步步解决啦 😊