【洛谷 P3367】【模板】并查集 题解(并查集+按秩合并)

【模板】并查集

题目描述

如题,现在有一个并查集,你需要完成合并和查询操作。

输入格式

第一行包含两个整数 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[N] 和 rnk[N],pre 用于存储每个元素的父节点,rnk 用于存储每个元素的秩(树的深度)。在初始化阶段,每个元素的父节点是其自身,秩为0。

函数 root 用于查找元素的根节点,也就是它所在集合的代表元素。通过不断地向上查找父节点,直到找到一个元素的父节点是其自身,那么就找到了该元素的根节点。

函数 merge 是并查集的核心操作,用于合并两个集合。首先找到两个元素的根节点,如果根节点相同,说明它们已经在同一个集合中,无需合并。否则,将秩较小的集合的根节点的父节点设置为秩较大的集合的根节点,这是为了保持树的平衡,避免树的深度过大。如果两个集合的秩相同,合并后新的集合的秩加一。

函数 check 用于检查两个元素是否在同一个集合中,也就是它们的根节点是否相同。如果根节点相同,输出 "Y",否则输出 "N"。

在 main 函数中,首先读取元素的数量 n 和操作的数量 m,然后初始化并查集。接下来进行 m 次操作,每次操作根据操作类型 z 是 1 还是 2,执行合并操作或者检查操作。

使用按秩合并优化后,代码运行用时大幅度缩短。


AC代码

cpp 复制代码
#include <algorithm>
#include <iostream>
#define AUTHOR "HEX9CF"
using namespace std;

const int N = 1e5 + 7;

int pre[N], rnk[N];

void init(int x) {
	for (int i = 1; i <= x; i++) {
		pre[i] = i;
	}
}

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 (rnk[x] > rnk[y]) {
		swap(x, y);
	}
	pre[x] = y;
	if (rnk[x] == rnk[y]) {
		rnk[y]++;
	}
}

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;
}
相关推荐
薛定e的猫咪几秒前
(ICLR 2026)DF-META:面向 FJSP 的无导数元学习实例自适应调度
人工智能·深度学习·学习·算法·机器学习
weixin_4173968333 分钟前
桌面云终端PXE启动失败怎么排查?从DHCP Option到镜像下发
开发语言·前端·php·技术·酒店·综合布线·弱电
某不知名網友1 小时前
ROS2进阶:服务通信、自定义SRV接口、参数服务器与基于服务的参数通信详解
java·开发语言·microsoft
傲世仙尊1 小时前
string增删查改-insert-erase-replace与find加substr组合技
c++
java_nnnn1 小时前
LeetCode刷题滑动窗口(水果成篮+找到字符串中所有字母的异位词)Java
java·开发语言·leetcode
qetfw2 小时前
Bash 字符串处理:参数展开、序列与脚本执行方式
linux·开发语言·chrome·bash
小羊没烦恼!2 小时前
异步编程:线程概述及使用
java·开发语言·mysql·算法·c#
wanderist.2 小时前
202609CSP认证P3
算法
学Linux的语莫2 小时前
python中类的常见写法
开发语言·python·状态模式
需要8262 小时前
容器化 Java:镜像瘦身与 JVM 容器感知(cgroup)
java·开发语言·jvm