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\) 行,每行一个 YesNo。表示第 \(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;
}

代码解释:

  1. 并查集类 UnionFind
    • 构造函数 UnionFind(int n):初始化并查集,将每个人的父节点设为自身,范围从 1n
    • 查找函数 find(int x):使用路径压缩的方式查找元素 x 的根节点。
    • 合并函数 unionSet(int x, int y):将 xy 所在的集合合并。
    • 判断连通性函数 isConnected(int x, int y):判断 xy 是否在同一个集合中,即是否为亲戚。
  2. 主函数 main()
    • 读取输入的人数 n、亲戚关系数量 m 和询问数量 p
    • 创建并查集对象 uf
    • 读取 m 个亲戚关系,调用 unionSet 函数将对应的两人所在集合合并。
    • 读取 p 个询问,调用 isConnected 函数判断两人是否为亲戚,并输出相应结果。

复杂度分析:

  • 时间复杂度:初始化并查集的时间复杂度为 ,每次合并和查找操作的平均时间复杂度接近 ,因此总的时间复杂度为 。
  • 空间复杂度:主要用于存储并查集的 parent 数组,空间复杂度为 。
posted @ 2025-02-17 17:59  ToFuture$  阅读(38)  评论(1)    收藏  举报