1. 并查集基础与问题背景
第一次接触并查集(Disjoint Set Union)数据结构时,我被它的简洁高效所震撼。这个看似简单的数据结构,却能优雅解决许多复杂的连通性问题。让我们以P1892 [BalticOI 2003]团伙问题为例,深入探讨并查集的实际应用。
并查集的核心功能是管理元素的分类与合并。它主要支持两种操作:
- Find:查找元素所属集合
- Union:合并两个集合
在团伙问题中,我们需要处理两类关系:
- 朋友关系:具有传递性(A-B且B-C ⇒ A-C)
- 敌人关系:具有对称性(A是B的敌人 ⇒ B是A的敌人)且敌人关系会形成"敌人的敌人是朋友"的连锁反应
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据结构设计与优化
2.1 基础并查集实现
最基础的并查集使用数组表示父节点关系:
cpp复制int parent[MAXN];
void init(int n) {
for(int i=1; i<=n; ++i)
parent[i] = i;
}
int find(int x) {
if(parent[x] == x) return x;
return find(parent[x]);
}
void unite(int x, int y) {
x = find(x);
y = find(y);
if(x != y) parent[y] = x;
}
这种实现存在明显效率问题:find操作在最坏情况下是O(n)复杂度。我们需要优化。
2.2 路径压缩优化
通过在查找过程中扁平化树结构,可以大幅提升效率:
cpp复制int find(int x) {
return parent[x] == x ? x : (parent[x] = find(parent[x]));
}
这个简单的修改让后续查询复杂度降至接近O(1)。
2.3 按秩合并
另一种优化是记录树的深度,总是将小树合并到大树下:
cpp复制int rank[MAXN]; // 初始化为0
void unite(int x, int y) {
x = find(x);
y = find(y);
if(x == y) retu
