给定一个包含n个点(编号为1~n)的无向图,初始时图中没有边。
现在要进行m个操作,操作共有三种:
- “C a b”,在点a和点b之间连一条边,a和b可能相等;
- “Q1 a b”,询问点a和点b是否在同一个连通块中,a和b可能相等;
- “Q2 a”,询问点a所在连通块中点的数量;
输入格式
第一行输入整数n和m。
接下来m行,每行包含一个操作指令,指令为“C a b”,“Q1 a b”或“Q2 a”中的一种。
输出格式
对于每个询问指令”Q1 a b”,如果a和b在同一个连通块中,则输出“Yes”,否则输出“No”。
对于每个询问指令“Q2 a”,输出一个整数表示点a所在连通块中点的数量
每个结果占一行。
数据范围
1≤n,m≤105
输入样例:
5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5
输出样例:
Yes
2
3
Go 代码
package main
import (
"bufio"
"fmt"
"io"
"os"
"strconv"
"strings"
)
func main() {
// file, _ := os.Open("./1.txt")
// defer file.Close()
new(os.Stdin)
var n, m int
v := readLine()
n, _ = strconv.Atoi(v[0]) // 读取点的数量和操作的数量
m, _ = strconv.Atoi(v[1])
p := make([]int, n+1) // 存储每个元素它的父节点
size := make([]int, n+1) // 每个集合点的数量
for i := 1; i <= n; i++ { // 初始的时候每个元素就是单个的集合
p[i] = i // 树根,父节点就是它自己
size[i] = 1 // 每个集合都只有一个点
}
for i := 0; i < m; i++ { // m 次操作
v = readLine()
op := v[0]
if op == "C" {
x, _ := strconv.Atoi(v[1])
y, _ := strconv.Atoi(v[2])
if find(p, x) == find(p, y) {
continue
}
size[find(p, y)] += size[find(p, x)]
p[find(p, x)] = find(p, y)
} else if op == "Q1" {
x, _ := strconv.Atoi(v[1])
y, _ := strconv.Atoi(v[2])
if find(p, x) == find(p, y) {
fmt.Println("Yes")
} else {
fmt.Println("No")
}
} else { // 询问点所在连通块中点的数量
x, _ := strconv.Atoi(v[1])
fmt.Println(size[find(p, x)])
}
}
}
// 返回 x 的祖宗节点 + 路径压缩
func find(p []int, x int) int {
if p[x] != x { // 如果不是根节点
p[x] = find(p, p[x]) // 就让这个节点等于它的祖宗节点
}
return p[x] // 最后返回它的父节点
}
var scanner *bufio.Scanner
func new(reader io.Reader) {
scanner = bufio.NewScanner(reader)
bs := make([]byte, 20000*1024)
scanner.Buffer(bs, len(bs))
}
func readLine() []string {
scanner.Scan()
return strings.Split(scanner.Text(), " ")
}