AcWing 836. 合并集合

204 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

AcWing 836. 合并集合

一共有 nn 个数,编号是 1∼n,最开始每个数各自在一个集合中。

现在要进行 mm 个操作,操作共有两种:

  1. M a b,将编号为 aa 和 bb 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;
  2. Q a b,询问编号为 aa 和 bb 的两个数是否在同一个集合中;

输入格式

第一行输入整数 n 和 m。

接下来 m 行,每行包含一个操作指令,指令为 M a b 或 Q a b 中的一种。

输出格式

对于每个询问指令 Q a b,都要输出一个结果,如果 aa 和 bb 在同一集合内,则输出 Yes,否则输出 No

每个结果占一行。

数据范围

1 ≤ n,m ≤ 10^5

输入样例:

4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4

输出样例:

Yes
No
Yes

思路

并查集

并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。

开始时每个集合都是一个独立的集合,并且都是等于自己本身下标的数 例如: p[5]=5,p[3]=3; 如果是M操作的话那么就将集合进行合并,合并的操作是: p[3]=p[5]=5; 所以3的祖宗节点便成为了5 此时以5为祖宗节点的集合为{5,3} 如果要将p[9]=9插入到p[3]当中,应该找到3的祖宗节点, 然后再把p[9]=9插入其中,所以p[9]=find(3);(find()函数用于查找祖宗节点) 也可以是p[find(9)]=find(3),因为9的节点本身就是9 此时以5为祖宗节点的集合为{5,3,9}; 如果碰到多个数的集合插入另一个集合当中其原理是相同的 例如: 上述中以5为祖宗节点的是p[5],p[3],p[9];(即p[5]=5,p[3]=5,p[9]=5) 再构造一个以6为祖宗节点的集合为{6,4,7,10} 如果要将以6为祖宗节点的集合插入到以5为祖宗节点的集合,则该操作可以是 p[6]=find(3)(或者find(9),find(5)) 此时p[6]=5 当然如果是以6为祖宗节点集合中的4,7,10则可以这样 p[find(4)]=find(3) 或者p[find(7)]=find(3)均可以 此时以6为祖宗节点的集合的祖宗节点都成为了5

AC代码

#include<iostream>
using namespace std;
const int N = 100001;
int p[N];
int n,m;
int find(int x){//返回x所在集合的编号(祖宗节点)路径压缩
    if(p[x] != x) p[x] = find(p[x]);//如果当前节点不是根节点,就让其父节点等于他的祖宗节点
    return p[x];//最后返回其父节点
}
int main(){
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) p[i] = i;
    while(m--){
        char op[2];
        int a,b;
        scanf("%s%d%d", op, &a, &b);
        if(*op == 'M'){
            p[find(a)] = find(b);// a节点认b当爹
        } 
        else{
            if(find(a) == find(b)) {
                puts("Yes");
            }
            else {
                puts("No");
            }
        }
    }
    return 0;
}