算法1
($\mathcal{Tarjin}$缩点) $\mathcal O(n+m)$
本题翻译一下就是找出满足在反图中可以到达所有点的点。
$\mathbb 1$. 暴力做法直接建反图,枚举每个点看能否到达所有点,时间复杂度为平方级别,略高。
$\mathbb 2$. 优化做法是利用tarjin算法先将图缩点变成有向无环图,则在该图中,出度为0的强连通分量若只有一个,则说明有解,否则无解,而解的数量就是该出度为0的强连通分量的点的个数。
$\mathbb 3$. Tarjin算法复习:核心是通过dfs记录时间戳的方式,看能否遇见之前访问过的时间戳,遇到了说明就成环了,更新一路的时间戳。还需要通过栈来保存dfs访问节点的顺序,当碰到一个强连通分量的顶点后,就可以将此强连通分量出栈。
$\mathbb 4$. Tarjin重点数组:dfn保存节点的实际访问时间戳,low保存节点及它所有的子孙节点可以访问到的最小时间戳,若无环则dfn都等于low,并递增,若有环,则low要被更新为之前访问某点的dfn,因此会变小。stk保存所有还未形成强连通分量的点,instk标记点是否还在栈中。还需要视题目要求保存每个分量内点的个数,每个点对应哪个分量。同时最重要的一点,由于dfs的性质,最先形成的强连通分量一定在拓扑图中排最后,所以强连通分量的逆序即是拓扑序,这个性质可以保证不需要再进行拓扑排序了。
Golang 代码
package main
import (
"os"
"bufio"
"strings"
"strconv"
"fmt"
)
func main() {
sc := bufio.NewScanner(os.Stdin)
buf := make([]byte, 1024*1024)
sc.Buffer(buf, len(buf))
sc.Scan()
str := strings.Fields(sc.Text())
n, _ := strconv.Atoi(str[0])
m, _ := strconv.Atoi(str[1])
head, next, to := make([]int, n+1), make([]int, m+1), make([]int, m+1)
cnt := 0
scc_cnt := 0
timestamp := 0
dfn, low := make([]int, n+1), make([]int, n+1)
var stk []int
instk := make([]bool, n+1)
var size []int
id := make([]int, n+1)
add := func(a, b int) {
cnt++
next[cnt], to[cnt], head[a] = head[a], b, cnt
}
for i := 1; i <= m; i++ {
sc.Scan()
str = strings.Fields(sc.Text())
a, _ := strconv.Atoi(str[0])
b, _ := strconv.Atoi(str[1])
add(a, b)
}
var tarjin func(int)
tarjin = func(h int) {
timestamp++
dfn[h], low[h] = timestamp, timestamp
stk = append(stk, h)
instk[h] = true
for i := head[h]; i != 0; i = next[i] {
tot := to[i]
if dfn[tot] == 0 {
tarjin(tot)
low[h] = min(low[h], low[tot])
} else if instk[tot] {
low[h] = min(low[h], dfn[tot])
}
}
if dfn[h] == low[h] {
size = append(size, 0)
for {
top := stk[len(stk)-1]
stk = stk[:len(stk)-1]
size[len(size)-1]++
id[top] = scc_cnt
instk[top] = false
if top == h {
break
}
}
scc_cnt++
}
}
for i := 1; i <= n; i++ {
if dfn[i] == 0 {
tarjin(i)
}
}
deg := make([]int, scc_cnt)
for i := 1; i <= n; i++ {
for j := head[i]; j != 0; j = next[j] {
tot := to[j]
if id[i] != id[tot] {
deg[id[i]]++
}
}
}
ans_cnt := 0
ans := 0
for i := 0; i < scc_cnt; i++ {
if deg[i] == 0 {
ans_cnt++
ans = size[i]
}
}
if ans_cnt == 1 {
fmt.Println(ans)
} else {
fmt.Println(0)
}
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a > b {
return b
}
return a
}
func abs(a int) int {
if a > 0 {
return a
}
return -a
}