题目描述
一共有 n 个数,编号是 1∼n,最开始每个数各自在一个集合中。
现在要进行 m 个操作,操作共有两种:
1.M a b,将编号为 a 和 b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;
2.Q a b,询问编号为 a 和 b 的两个数是否在同一个集合中;
输入格式
第一行输入整数 n 和 m。
接下来 m 行,每行包含一个操作指令,指令为 M a b 或 Q a b 中的一种。
输出格式
对于每个询问指令 Q a b,都要输出一个结果,如果 a 和 b 在同一集合内,则输出 Yes,否则输出 No。
每个结果占一行。
数据范围
1≤n,m≤105
输入样例:
4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4
输出样例:
Yes
No
Yes
题目分析
1.用树来维护集合
2.根节点的编号是集合编号
3.p[x]表示x的父节点
1.如何判断是否为根结点:if(p[x]=x),只有根结点的父节点与自身相等
2.如何找到根结点:while(p[x]!=x)x=p[x],向上走直到找到根结点,如果x,y的根结点相同,则表示在一个集合当中,
3.如何和并集合:将x树挂载到y树上,假设有两棵树一个根节点是x,一个根节点是y,令p[x] = y,这样两个集合当中的每个元素的祖宗结点都为y,就实现了集合合并。
解决方案
import java.util.*;
public class Main{
static int N = 100010,n,m;
//存储每个节点的父节点
static int[] p = new int[N];
//找到祖宗结点+路径压缩(即每一个父节点都等于祖宗结点)
static int find(int x){
if(p[x]!=x) p[x] = find(p[x]);
return p[x];
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
String s[] = sc.nextLine().split(" ");
n = Integer.parseInt(s[0]);
m = Integer.parseInt(s[1]);
//初始化
for(int i=1;i<=n;i++){
p[i] = i;
}
while(m-->0){
String str[] = sc.nextLine().split(" ");
int x=Integer.parseInt(str[1]);
int y=Integer.parseInt(str[2]);
if("M".equals(str[0])){
//将x树挂到y的根节点上
p[find(x)] = find(y);
}else{
if(find(x)==find(y)) System.out.println("Yes");
else System.out.println("No");
}
}
}
}