P1551 亲戚——并查集【模板】
题目背景
若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。
题目描述
规定:\(x\) 和 \(y\) 是亲戚,\(y\) 和 \(z\) 是亲戚,那么 \(x\) 和 \(z\) 也是亲戚。如果 \(x\),\(y\) 是亲戚,那么 \(x\) 的亲戚都是 \(y\) 的亲戚,\(y\) 的亲戚也都是 \(x\) 的亲戚。
输入格式
第一行:三个整数 \(n,m,p\),(\(n,m,p \le 5000\)),分别表示有 \(n\) 个人,\(m\) 个亲戚关系,询问 \(p\) 对亲戚关系。
以下 \(m\) 行:每行两个数 \(M_i\),\(M_j\),\(1 \le M_i,~M_j\le n\),表示 \(M_i\) 和 \(M_j\) 具有亲戚关系。
接下来 \(p\) 行:每行两个数 \(P_i,P_j\),询问 \(P_i\) 和 \(P_j\) 是否具有亲戚关系。
输出格式
\(p\) 行,每行一个 Yes 或 No。表示第 \(i\) 个询问的答案为“具有”或“不具有”亲戚关系。
输入输出样例 #1
输入 #1
6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6
输出 #1
Yes
Yes
No
题解
这是一个典型的并查集应用问题,我们可以使用并查集来解决判断两个人是否为亲戚的问题。以下是使用 C++ 实现的代码:
#include <iostream>
#include <vector>
using namespace std;
class UnionFind {
private:
vector<int> parent;
public:
UnionFind(int n) {
parent.resize(n + 1);
for (int i = 1; i <= n; ++i) {
parent[i] = i;
}
}
int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
void unionSet(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY) {
parent[rootX] = rootY;
}
}
bool isConnected(int x, int y) {
return find(x) == find(y);
}
};
int main() {
int n, m, p;
cin >> n >> m >> p;
UnionFind uf(n);
// 处理亲戚关系,进行合并操作
for (int i = 0; i < m; ++i) {
int Mi, Mj;
cin >> Mi >> Mj;
uf.unionSet(Mi, Mj);
}
// 处理询问
for (int i = 0; i < p; ++i) {
int Pi, Pj;
cin >> Pi >> Pj;
if (uf.isConnected(Pi, Pj)) {
cout << "Yes" << endl;
} else {
cout << "No" << endl;
}
}
return 0;
}
代码解释:
- 并查集类
UnionFind:- 构造函数
UnionFind(int n):初始化并查集,将每个人的父节点设为自身,范围从1到n。 - 查找函数
find(int x):使用路径压缩的方式查找元素x的根节点。 - 合并函数
unionSet(int x, int y):将x和y所在的集合合并。 - 判断连通性函数
isConnected(int x, int y):判断x和y是否在同一个集合中,即是否为亲戚。
- 构造函数
- 主函数
main():- 读取输入的人数
n、亲戚关系数量m和询问数量p。 - 创建并查集对象
uf。 - 读取
m个亲戚关系,调用unionSet函数将对应的两人所在集合合并。 - 读取
p个询问,调用isConnected函数判断两人是否为亲戚,并输出相应结果。
- 读取输入的人数
复杂度分析:
- 时间复杂度:初始化并查集的时间复杂度为 ,每次合并和查找操作的平均时间复杂度接近 ,因此总的时间复杂度为 。
- 空间复杂度:主要用于存储并查集的
parent数组,空间复杂度为 。
浙公网安备 33010602011771号