代码 / OI

Tarjan

6 分钟阅读
代码OITarjan算法

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

#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 ; 
}