Golang算法模板-并查集

101 阅读2分钟

并查集模板

https://www.qduoj.com/problem/586

Description Z学长课间时总是会玩一些小游戏休闲片刻。这天,Z学长找到了这样的一个小游戏:开始时有n个石子(编号从1—n)从左至右依次排开,每个石子各自带有一个权值。有两种操作: 1.将编号为x的石子所在的堆和它那堆右面的堆合并为一堆,记作 “ M x ”,若该堆右侧无其它堆则忽略此次操作; 2.询问编号为x的石子所在的堆中权值最小的石子的权值为多少,记作“ Q x ”; 刚开始时由于所给石子数量过少,所以Z学长仍能应对自如,但越往后的关卡中所给的石子数越来越多,Z学长渐觉力不从心,因此特来求助于聪明的你,聪明的你能否帮学长度过难关?

Input 第1行输入n,q(n<=105,q<=105),表示石子的个数与询问的次数; 第2行给出n个值c[1]~c[n],ci表示编号为i的石子的权值; 其余q行,每行给出题中所描述的两种操作中的一种操作。

Output 对于每个“ Q x ” 询问操作,在单独的一行给出该询问的答案。

Sample Input 1 6 4 1 1 2 3 5 8 Q 1 M 3 M 3 Q 5

Sample Output 1 1 2

解题思路:

以权值最少的那一堆作为根节点合并 从t开始到n-1,如果t到t+1在一个堆里就继续,如果不在一个堆里就合并并且跳出循环。

package main
​
import "fmt"
​
var pre = [100005]int{}
var c = [100005]int{}
​
func find(x int) int {
    if pre[x] == x {
        return pre[x]
    } else {
        pre[x] = find(pre[x])
        return pre[x]
    }
}
func join(a, b int) {
    fa := find(a)
    fb := find(b)
    if fa != fb && c[fa] < c[fb] {
        pre[fb] = fa
    } else {
        pre[fa] = fb
    }
}
func main() {
    var n, q int
    fmt.Scan(&n, &q)
    for i := 1; i <= n; i++ {
        fmt.Scan(&c[i])
        pre[i] = i
    }
    for i := 1; i <= q; i++ {
        var str string
        var t int
        fmt.Scan(&str, &t)
        fmt.Println(str, t)
        if str[0] == 'M' {
            for ; t <= n-1; t++ {
                if find(t) != find(t+1) {
                    join(t, t+1)
                    break
                }
            }
        } else {
            fmt.Printf("%d\n", c[find(t)])
        }
    }
}