1. 程式人生 > 實用技巧 >Luogu P2863 [USACO06JAN]The Cow Prom S

Luogu P2863 [USACO06JAN]The Cow Prom S

思路

這個題就是純正的Tarjan模板題,關於難以理解的、玄學的low陣列,建議感性理解。網上的解釋千姿百態,啥樣的都有,有的對有的錯,看多了反而會暈。所以建議Tarjan模板基本的幾個部分多打幾遍,

熟練了就好(不建議強求理解)。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXN 20010
#define MAXM 50010
int n, m, idx;
int head[MAXM], cnt;
int dfn[MAXN], low[MAXN];
int stk[MAXN], top, size[MAXN];
int flag[MAXN], bel[MAXN], scc;
struct node{
    int nxt, to;
} edge[MAXM];
inline int read(void){
    int f = 1, x = 0;char ch;
    do{ch = getchar();if(ch=='-')f = -1;} while (ch < '0' || ch > '9');
    do{ x = x * 10 + ch - '0';ch = getchar();} while (ch >= '0' && ch <= '9');
    return f * x;
}
inline int _min(int x,int y){
    return x < y ? x : y;
}
inline void add_edge(int x,int y){
    ++cnt;
    edge[cnt].nxt = head[x];
    edge[cnt].to = y;
    head[x] = cnt;
    return;
}
void tarjan(int k){
    dfn[k] = low[k] = ++idx;
    stk[++top] = k;
    flag[k] = 1;
    for (int i = head[k]; i; i = edge[i].nxt){
        int v = edge[i].to;
        if(!dfn[v])
            tarjan(v), low[k] = _min(low[k], low[v]);
        else if(flag[v]) low[k] = _min(low[k], dfn[v]);//這幾個min程式碼量也不大,建議背誦(大部分Tarjan題都不會對這兩個min做出變動)
    }
    if(dfn[k]==low[k]){
        int now = -1;
        ++scc;
        while(now!=k){
            now = stk[top], --top;
            flag[now] = 0;
            ++size[scc];
            bel[now] = scc;
        }//每次找到返祖邊(環)後統計答案
    }
}
int main(){
    n = read(), m = read();
    for (int i = 1; i <= m; ++i){
        int u = read(), v = read();
        add_edge(u, v);//連有向邊
    }
    for (int i = 1; i <= n; ++i)
        if(!dfn[i]) tarjan(i);//注意原圖可能不連通
    // for (int i = 1; i <= n; ++i){
    //     printf("dfn[%d]=%d,low[%d]=%d\n", i, dfn[i], i, low[i]);
    // }
    int ans = 0;
    for (int i = 1; i <= scc; ++i)
        if(size[i]>1) ++ans;//統計大小大於1的強連通分量個數
    printf("%d\n", ans);
    return 0;
}