【种类并查集】洛谷 P1892 [BalticOI 2003] 团伙 (Day 2)
题目
https://www.luogu.com.cn/problem/P1892
题解
种类并查集又称为扩展域并查集,是并查集的一种扩展形式,主要用于处理多组对立关系或多种状态划分的问题。在处理种类并查集问题时,有多少种类,就得开几倍的空间,用于表示所有的种类。
由题目已知 \(n\) 个人,他们之间存在两种关系:朋友和敌人。且具备以下关系:
- 一个人的朋友的朋友是朋友
- 一个人的敌人的敌人是朋友
对于第 \(x\) 个人,不妨设其朋友的集合为 \(exdsu[x]\),敌人的集合为 \(exdsu[x+n]\)。可以画出其关系图为:

若第 \(x\) 个人和第 \(y\) 个人是朋友关系,可以画出其关系图为:

若第 \(x\) 个人和第 \(y\) 个人是敌人关系,可以画出其关系图为:

因为敌人关系不好表示,所以可以将其转化为朋友关系,使用种类并查集将所有朋友关系的集合合并起来。那么根据上述图表关系:
- 若第 \(x\) 个人和第 \(y\) 个人是朋友关系,根据朋友的朋友是朋友的关系,只需将 \(exdsu[x]\) 和 \(exdsu[x+n]\) 合并成一个集合;
- 若第 \(x\) 个人和第 \(y\) 个人是敌人关系,根据敌人的敌人是朋友的关系,只需分别将 \(exdsu[x]\) 与 \(exdsu[y+n]\) 和 \(exdsu[x+n]\) 与 \(exdsu[y]\) 合并为一个集合。
值得注意的编码小细节是,可以将较小者作为代表元,方便计算出答案。
参考代码
#include<bits/stdc++.h>
using namespace std;
constexpr int N = 2001;// 1000 个人,每个人至多有 2 种类别(朋友/敌人),因此需要开 1000 * 2 的空间
int n, m, u, v;
char r;// 关系
int exdsu[N];// 种类并查集
// 找到代表元
int find(int x) {
return x == exdsu[x] ? x : exdsu[x] = find(exdsu[x]);
}
// 合并集合
void merge(int x, int y) {// 将 y 作为代表元
exdsu[find(x)] = find(y);
}
int main() {
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
cin >> n >> m;
iota(exdsu + 1, exdsu + 1 + n * 2, 1);
while (m --) {
cin >> r >> u >> v;
if (r == 'F') {
merge(u, v);
} else {
merge(v + n, u);
merge(u + n, v);
}
}
int ans = 0;
for (int i = 1; i <= n; ++ i) {
if (i == find(i)) {
++ ans;
}
}
cout << ans;
return 0;
}
浙公网安备 33010602011771号