连通块中点的数量(并查集)
题目传送门:https://www.lanqiao.cn/problems/19873/learning/
一、题目重述
给定一个初始包含 n 个点(编号 1 到 n)的无向图,初始时没有边。需要进行 m 次操作,操作分为三种:
- C a b:在点 a 和点 b 之间连一条边(a 和 b 可能相同)。
- Q1 a b:查询点 a 和点 b 是否在同一个连通块中。
- Q2 a:查询点 a 所在连通块中点的数量。
要求高效处理这些操作,并输出相应的查询结果。
二、题目分析
- 操作类型:涉及动态的连通性维护(加边、查询连通性、查询连通块大小)。
- 数据范围:n 和 m 都是 1e5 级别,需要 O(α(n)) 或近似 O(1) 的算法。
- 关键点:快速合并集合、查询集合代表元、维护集合大小。
三、解题思路
这道题是典型的并查集(Disjoint Set Union, DSU)问题,适合用路径压缩和按秩合并优化,确保高效处理合并和查询操作。
四、算法讲解
1. 并查集(DSU)
- 用途:高效管理动态连通性(合并集合、查询是否连通)。
- 核心操作:
- find(x):找 x 的根(代表元),同时路径压缩优化。
- union(a, b):合并 a 和 b 所在集合,按秩合并优化。
- 优化:
- 路径压缩:
find(x)
时直接让 x 指向根,减少后续查询时间。 - 按秩合并:总让小树合并到大树上,减少树高。
- 路径压缩:
2. 维护连通块大小
- 额外开一个数组 siz,记录每个根节点对应的连通块大小。
- 合并时更新 siz:
siz[大根] += siz[小根]
。
3. 例子
- 初始:
fa = [1,2,3,4,5]
,siz = [1,1,1,1,1]
。 - C 1 2:合并 1 和 2,
fa = [2,2,3,4,5]
,siz = [1,2,1,1,1]
。 - Q1 1 2:
find(1) = find(2) = 2
→ Yes。 - Q2 1:
siz[find(1)] = siz[2] = 2
→ 输出 2。
五、代码实现
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
int fa[N]; // 并查集父节点数组
int n, m;
// 路径压缩的find函数
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
vector<int> siz(N, 1); // 记录每个连通块的大小,初始为1
// 合并函数(按秩合并)
void uni(int a, int b) {
if (a == b) return; // 相同点无需合并
a = find(a), b = find(b);
if (a == b) return; // 已在同一连通块
if (siz[a] > siz[b]) swap(a, b); // 确保a是小树
fa[a] = b; // 小树合并到大树
siz[b] += siz[a]; // 更新连通块大小
}
int main() {
// 初始化并查集
for (int i = 1; i < N; i++) fa[i] = i;
cin >> n >> m;
int a, b;
while (m--) {
string op;
cin >> op;
if (op == "Q1") { // 查询连通性
cin >> a >> b;
if (a == b) { // 相同点直接Yes
cout << "Yes" << "\n";
continue;
}
a = find(a), b = find(b);
cout << (a == b ? "Yes" : "No") << "\n";
} else if (op == "Q2") { // 查询连通块大小
cin >> a;
cout << siz[find(a)] << "\n";
} else { // 合并操作
cin >> a >> b;
uni(a, b);
}
}
return 0;
}
六、代码重点细节解释
- 路径压缩:
find(x)
函数通过递归直接让 x 指向根,降低后续查询复杂度。 - 按秩合并:通过
siz
数组比较树大小,确保小树合并到大树,避免退化。 - 初始化:
fa[i] = i
和siz[i] = 1
是并查集的标准初始化。 - 查询处理:
Q1
和Q2
直接调用find
和siz
数组,保证 O(1) 查询。
七、复杂度分析
- 时间复杂度:
find
和uni
操作均摊 O(α(n))(近似常数时间)。- 总复杂度:O(m α(n)),轻松通过 1e5 数据。
- 空间复杂度:O(n),用于存储
fa
和siz
数组。
八、关键点
- 并查集优化:路径压缩 + 按秩合并缺一不可。
- 维护额外信息:
siz
数组动态记录连通块大小。 - 特判自环:
a == b
时直接返回,避免无效操作。
九、总结
这道题是并查集的经典应用,考察动态维护连通性和集合大小的能力。核心在于:
- 熟练掌握并查集的路径压缩和按秩合并优化。
- 灵活维护额外信息(如连通块大小)。
- 注意边界条件(如自环、重复合并)。
十、励志文案
“代码如诗,算法似剑。在青春的赛道上,我们用逻辑编织梦想,用坚持铸就辉煌。每一次AC,都是对热爱的回响!” 🚀