洛谷P1551-亲戚-题解

亲戚

题目背景

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。

题目描述

规定:xxyy 是亲戚,yyzz 是亲戚,那么 xxzz 也是亲戚。如果 xxyy 是亲戚,那么 xx 的亲戚都是 yy 的亲戚,yy 的亲戚也都是 xx 的亲戚。

输入格式

第一行:三个整数 n,m,pn,m,p,(n,m,p5000n,m,p \le 5000),分别表示有 nn 个人,mm 个亲戚关系,询问 pp 对亲戚关系。

以下 mm 行:每行两个数 MiM_iMjM_j1Mi, MjN1 \le M_i,~M_j\le N,表示 MiM_iMjM_j 具有亲戚关系。

接下来 pp 行:每行两个数 Pi,PjP_i,P_j,询问 PiP_iPjP_j 是否具有亲戚关系。

输出格式

pp 行,每行一个 YesNo。表示第 ii 个询问的答案为“具有”或“不具有”亲戚关系。

样例 #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

P1551 亲戚 - 洛谷

思路

这题是 并查集 的模板题目。所谓并查集,就是一个具有合并和查询功能的森林,每一棵树都是是一个用父亲表示法的树,一个集合,这个集合里的元素具有某种关系。并查集的合并就是树和树之间合并的一个过程,而并查集的查询,我们都知道,在判断两个节点是否属于一棵树的时候,我们在可以使用判断他们的头节点是否相同来判断,并查集亦是如此。

知道这些基本概念,我们可以实现一个简单的并查集了。但需要注意的是,当我们森林中的树足够多的时候,频繁的合并可能会使查询的复杂度退化,为此,我们可以使用一些方法进行优化,这里直接贴出优化过的 并查集类模板 详细原理可以参考算法学习笔记(1) : 并查集

class UnionFindSet {
public:
    vector<int> trees, size;

    explicit UnionFindSet(int len) : trees(len), size(len, 1) {
        iota(trees.begin(), trees.end(), 0);
    }

    int find(const int &x) {
        int tmp;
        for (tmp = x; trees[tmp] != tmp; tmp = trees[tmp]);
        return tmp;
    }

    void unite(const int &x, const int &y) {
        int &&fx = find(x), &&fy = find(y);
        size[fx] <= size[fy] ? trees[fx] = fy : trees[fy] = fx;
        if (size[fx] == size[fy] and fx != fy)
            size[fy]++;
    }
};

这里用到了 iota 函数(注意不是 itoa )来快速填充了trees,填充范围为[0,[0,trees.size()))

C++代码

#include <iostream>
#include <vector>
#include <numeric>

using namespace std;

class UnionFindSet {
public:
    vector<int> trees, size;

    explicit UnionFindSet(int len) : trees(len), size(len, 1) {
        iota(trees.begin(), trees.end(), 0);
    }

    int find(const int &x) {
        int tmp;
        for (tmp = x; trees[tmp] != tmp; tmp = trees[tmp]);
        return tmp;
    }

    void unite(const int &x, const int &y) {
        int &&fx = find(x), &&fy = find(y);
        size[fx] <= size[fy] ? trees[fx] = fy : trees[fy] = fx;
        if (size[fx] == size[fy] and fx != fy)
            size[fy]++;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr), cout.tie(nullptr);
    int n, m, p, i, j;
    cin >> n >> m >> p;
    UnionFindSet ufs(n + 1);
    while (m-- and cin >> i >> j)
        ufs.unite(i, j);
    while (p-- and cin >> i >> j)
        cout << (ufs.find(i) == ufs.find(j) ? "Yes\n" : "No\n");
    return 0;
}

洛谷P1551-亲戚-题解
https://winterl-blog.netlify.app/2023/08/01/洛谷P1551-亲戚-题解/
作者
winterl
发布于
2023年8月1日
许可协议