【模板】并查集
题目描述
如题,现在有一个并查集,你需要完成合并和查询操作。
输入格式
第一行包含两个整数 N , M N,M N,M ,表示共有 N N N 个元素和 M M M 个操作。
接下来 M M M 行,每行包含三个整数 Z i , X i , Y i Z_i,X_i,Y_i Zi,Xi,Yi 。
当 Z i = 1 Z_i=1 Zi=1 时,将 X i X_i Xi 与 Y i Y_i Yi 所在的集合合并。
当
Z
i
=
2
Z_i=2
Zi=2 时,输出
X
i
X_i
Xi 与
Y
i
Y_i
Yi 是否在同一集合内,是的输出
Y
;否则输出 N
。
输出格式
对于每一个
Z
i
=
2
Z_i=2
Zi=2 的操作,都有一行输出,每行包含一个大写字母,为 Y
或者 N
。
样例 #1
样例输入 #1
4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4
样例输出 #1
N
Y
N
Y
提示
对于 30 % 30\% 30% 的数据, N ≤ 10 N \le 10 N≤10, M ≤ 20 M \le 20 M≤20。
对于 70 % 70\% 70% 的数据, N ≤ 100 N \le 100 N≤100, M ≤ 1 0 3 M \le 10^3 M≤103。
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 4 1\le N \le 10^4 1≤N≤104, 1 ≤ M ≤ 2 × 1 0 5 1\le M \le 2\times 10^5 1≤M≤2×105, 1 ≤ X i , Y i ≤ N 1 \le X_i, Y_i \le N 1≤Xi,Yi≤N, Z i ∈ { 1 , 2 } Z_i \in \{ 1, 2 \} Zi∈{1,2}。
思路
定义一个数组pre
用于存储每个元素的父节点,sz
数组用于存储每个集合的大小。在初始化时,每个元素都是一个单独的集合,父节点是自己,集合的大小为1。
root
函数用于查找元素的根节点,也就是找到该元素所在集合的代表元素。这个过程是通过不断地向上查找父节点来实现的。
merge
函数用于合并两个集合。先找到两个集合的根节点,如果根节点相同,说明两个集合已经是同一个集合,不需要合并。如果不同,就将较小的集合合并到较大的集合中,并更新大集合的大小。
check
函数用于检查两个元素是否在同一个集合中。如果两个元素的根节点相同,说明它们在同一个集合中,打印“Y”,否则打印“N”。
在主函数中,首先读取元素的数量n
和操作的数量m
,然后初始化并查集。接下来进行m
次操作,每次操作根据读取的操作类型z
来执行合并集合或者检查元素是否在同一个集合中的操作。
使用启发式合并优化后,代码运行用时大幅度缩短。
AC代码
#include <algorithm>
#include <iostream>
#define AUTHOR "HEX9CF"
using namespace std;
const int N = 1e5 + 7;
int pre[N], sz[N];
void init(int x) {
for (int i = 1; i <= x; i++) {
pre[i] = i;
sz[i] = 1;
}
}
int root(int x) {
int i = x;
while (pre[i] != i) {
i = pre[i];
}
return i;
}
void merge(int x, int y) {
x = root(x);
y = root(y);
if (x == y) {
return;
}
if (sz[x] > sz[y]) {
swap(x, y);
}
pre[x] = y;
sz[y] += sz[x];
}
void check(int x, int y) {
x = root(x);
y = root(y);
if (x == y) {
printf("Y\n");
} else {
printf("N\n");
}
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
init(n);
while (m--) {
int z, x, y;
scanf("%d %d %d", &z, &x, &y);
if (z == 1) {
merge(x, y);
} else {
check(x, y);
}
}
return 0;
}