想当年高中组织活动的时候我还专门写过 Tarjan 的流程和证明,如今已经忘的干干净净,原来的代码也找不到了,只能现写了。
总的来说就是利用一个栈,将每个访问到的点 push 入栈,在寻找的过程中记录两个值,一个是 dfn—— 它是第几个搜索到的,一个是 low—— 它能衍生的点中 dfn 最小的值。如果一个点 dfn==low 说明它本身就是最小的点,把它及它栈以上的点全部 pop 出来就行,因为它上面的点必定是与它形成强联通分量。我们不妨假设它上面的点有不是它强联通分量的,那么此点在回溯到它本身的时候只有两种情况,一是它本身是强联通最小的点,那么在找到此点时会把它上面的点全部 pop 出去,另一种是它不是最小点,那么在遍历的过程中总会到第一种情况把它排除。
Tarjan 程序是正确无误的,然后最后要跑一个拓扑,然鹅我不大会。luogu 上爆了 40pt,然后去看别人的题解秒懂。用拓扑可以优化掉 ans、Q、new_val 等等数组,估计就 80pt 了,然后我们还得用 dp 去解决菊花图这种类型的数据。
这道题还给人一个教训是,对于缩点的题,我们记录下每条路径的开始与结束去构造新图,没必要单独列个 Q 这种数组。
40pt 代码如下:
(其实会个 Tarjan 就行了吧)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std ;
const int maxn = 100005 ;
int n , m ;
int head[maxn] , val[maxn] , nex[maxn] , cnt , to[maxn] , group[10004] ;
int tot , ind , a[10004] , stac[10004] , dfn[10004] , vis[10004] , low[10004] ;
queue < int > Q[10004] ; int col , in[10004] , new_val[10004] ;
int ans[10004] , num ;
void add ( int x , int y ) { nex[++cnt] = head[x] ; head[x] = cnt ; to[cnt] = y ; }

void Tarjan ( int u ) {
dfn[u] = low[u] = ++ tot ; vis[u] = 1 ; stac[++ind] = u ;
for ( int i = head[u] ; i ; i = nex[i] ) {
if ( ! dfn[to[i]] ) {
Tarjan ( to[i] ) ;
low[u] = min ( low[u] , low[to[i]] ) ;
}
else if ( vis[to[i]] ) {
low[u] = min ( low[u] , low[to[i]] ) ;
}
}
if ( low[u] == dfn[u] ) {
int v ; col ++ ;
while ( v = stac[ind--] ) {
Q[col].push ( v ) ; group[v] = col ;
vis[v] = 0 ;
if ( v == u ) break ;
}
}
}

void dfs ( int x , int val ) {
int flag = 0 ;
for ( int i = head[x] ; i ; i ++ ) {
flag = 1 ;
dfs ( to[i] , val + new_val[to[i]] ) ;
}
if ( ! flag ) {
ans[++num] = val ;
}
}

void topo ( ) {
for ( int i = 1 ; i <= col ; i ++ ) {
if ( in[i] == 0 ) {
dfs ( i + n , new_val[i] ) ;
}
}
}

bool cmp ( int x , int y ) { return x > y ; }

int main ( ) {
scanf ( "%d%d" , & n , & m ) ;
for ( int i = 1 ; i <= n ; i ++ ) scanf ( "%d" , & a[i] ) ;
while ( m -- ) {
int x , y ; scanf ( "%d%d" , & x , & y ) ;
add ( x , y ) ;
}
for ( int i = 1 ; i <= n ; i ++ ) {
if ( ! dfn[i] ) Tarjan ( i ) ;
}
for ( int i = 1 ; i <= col ; i ++ ) {
int new_node = i + n , val = 0 ;
while ( ! Q[i].empty ( ) ) {
int x = Q[i].front ( ) ; Q[i].pop ( ) ; val += a[x] ;
for ( int j = head[x] ; j ; j = nex[j] ) {
if ( group[to[j]] == group[x] ) continue ;
add ( new_node , group[to[j]] + n ) ;
in[to[j]] ++ ;
}
}
new_val[i] = val ;
}
topo ( ) ;
sort ( ans + 1 , ans + 1 + num , cmp ) ;
printf ( "%d" , ans[1] ) ;
return 0 ;
}

更新于 阅读次数