1. 问题背景与核心思路
这道NOI经典题目"食物链"描述了一个生态系统中三类生物(A吃B,B吃C,C吃A)的相互关系。我们需要处理两种操作:
- 声明X和Y属于同一物种
- 声明X捕食Y
同时需要识别其中的矛盾陈述(即与之前已确立的关系冲突的声明)。这类关系维护问题,正是并查集的拿手好戏。
关键难点在于:普通并查集只能处理"属于同一集合"的关系,而这里需要表达三种生物之间的复杂关系链。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 两种主流解法对比
2.1 带权并查集实现方案
带权并查集通过在节点间维护相对关系来解决这个问题。我们定义:
- 0:与父节点同类
- 1:被父节点吃
- 2:吃父节点
路径压缩时需要动态维护这些权值。合并集合时,通过向量运算推导新关系:
cpp复制int find(int x) {
if (fa[x] == x) return x;
int root = find(fa[x]);
val[x] = (val[x] + val[fa[x]]) % 3;
return fa[x] = root;
}
void unite(int x, int y, int relation) {
int fx = find(x), fy = find(y);
if (fx == fy) return;
fa[fy] = fx;
val[fy] = (val[x] - val[y] + relation + 3) % 3;
}
2.2 扩展域并查集实现方案
将每个动物拆分为三个域:
- self域:表示自身种类
- eat域:表示捕食对象
- enemy域:表示天敌
当声明X和Y同类时,需要合并:
- X的self域和Y的self域
- X的eat域和Y的eat域
- X的enemy域和Y的enemy域
声明X吃Y时,需要合并:
- X的self域和Y的enemy域
- X的eat域和Y的self域
- X的enemy域和Y的eat域
cpp复制void unite(int x, int y) {
fa[find(x)] = find(y);
}
bool same(int x, int y) {
return find(x) == find(y);
}
// 处理同类声明
if (same(x, y + n) || same(x, y + 2*n)) {
// 矛盾处理
} else {
unite(x, y);
unite(x + n, y + n);
unite(x + 2*n, y + 2*n);
}
3. 完整带权并查集实现
cpp复制#include <iostream>
using namespace std;
const int MAXN = 50010;
int fa[MAXN], val[MAXN];
int n, k, ans;
int find(int x) {
if (fa[x] == x) return x;
int root = find(fa[x]);
val[x] = (val[x] + val[fa[x]]) % 3;
return fa[x] = root;
}
int main() {
cin >> n >> k;
for (int i = 1; i <= n; ++i) fa[i] = i;
while (k--) {
int op, x, y;
cin >> op >> x >> y;
if (x > n || y > n) {
ans++;
continue;
}
int fx = find(x), fy = find(y);
if (op == 1) { // 同类声明
if (fx == fy && val[x] != val[y]) {
ans++;
} else if (fx != fy) {
fa[fy] = fx;
val[fy] = (val[x] - val[y] + 3) % 3;
}
} else { // 捕食声明
if (x == y) {
ans++;
continue;
}
if (fx == fy && (val[x] + 1) % 3 != val[y]) {
ans++;
} else if (fx != fy) {
fa[fy] = fx;
val[fy] = (val[x] - val[y] + 4) % 3;
}
}
}
cout << ans << endl;
return 0;
}
4. 关键问题与调试技巧
4.1 常见错误点
- 模数运算错误:关系运算必须严格模3
- 路径压缩时权值更新顺序:必须先递归再计算
- 合并时的向量方向:注意x和y的相对关系
4.2 测试用例设计
建议验证这些边界情况:
- 自相矛盾的声明(X吃X)
- 长关系链的传递(A吃B,B吃C,则C应该吃A)
- 大量随机操作的稳定性
4.3 性能优化
- 使用按秩合并可以进一步优化性能
- 输入输出使用更快的读写方式(如scanf/printf)
- 对于极限数据,可以预先分配足够内存
5. 算法扩展思考
这类带权并查集可以推广到更复杂的关系网络:
- 多物种食物链(N种生物形成循环)
- 非对称关系(如A吃B但B不吃C)
- 动态关系变更(允许撤销之前的声明)
实际应用中,这种技术可以用于:
- 社交网络的信任关系建模
- 分布式系统的一致性检查
- 知识图谱的关系推理
