#include<bits/stdc++.h>
using namespace std;

const int N = 10010, M = 50010;

int n, m;
int h[N], e[M], ne[M],idx;
int dfn[N], low[N], timestamp;
int stk[N], top;
bool in_stk[N];
int id[N], scc_cnt, Size[N];
int d[N];

void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

void tarjan(int u)
{
    dfn[u] = low[u] = ++ timestamp;
    stk[++top] = u, in_stk[u] = true;

    for(int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if(!dfn[j]) //j没有被遍历过
        {
            tarjan(j);
            low[u] = min(low[u], low[j]);
        }
        else if(in_stk[j])
        {
            low[u] = min(low[u], dfn[j]);
        }
    }



    if(dfn[u] == low[u])
    {
        int y;
        ++ scc_cnt;
        do
        {
            y = stk[top --];
            in_stk[y] = false;
            id[y] = scc_cnt;
            Size[scc_cnt] ++;
        }while(y != u);
    }
}


int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof h);
    while(m --)
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
    }

    for(int i = 1; i <= n; i++)
        if(!dfn[i]) tarjan(i);
    
    //缩点后的出度
    for(int i = 1; i <= n; i++)
        for(int j = h[i]; j != -1; j = ne[j])
        {
            int k = e[j];
            int a = id[i], b = id[k]; //a, b为点i,k所在连通分量的编号
            if(a != b) d[a] ++;
        }
    int sum = 0, ans = 0;
    for(int i = 1; i <= scc_cnt; i++)
    {
        if(d[i] == 0)
        {
            sum ++;
            ans += Size[i];
            if(sum > 1) {
                ans = 0;
                break;
            }
        }
    }
    cout << ans;
    return 0;
}


/*
有向图的连通分量: 对于一个有向图,其中分量中任意两点u和v,必然可以从u走到v,且从v走到u
强连通分量:极大连通分量

tarjan算法求强连通分量(SCC)

实现方式:DFS

对于每个点定义两个时间戳: 
dfn[u]:表示遍历到u这个点的时间戳
low[u]:从u开始走,所能遍历到的最小时间戳是什么
low[u] == dfn[u] 等价为 u是其所在的强连通分量的最高点


四种边:
1.树枝边(x,y): x是y的父节点 x->y   dfn[y] > dfn[x]  low[u] > dfn[u]
2.前向边(x,y): x是y的祖先节点 x->...->y dfn[y] > dfn[x] low[u] > dfn[u] 
3.后向边(x,y):  x是y的祖先节点,y->x  dfn[x] > dfn[y] 后向边终点 dfn[u]==low[u]
4.横叉边(x,y):  x是访问过的点 dfn[x] > dfn[y]

常用场景: 缩点

连通分量编号递减的顺序一定拓扑序



对于本题:
当一个强连通分量的出度为0,则该强连通分量的所有牛都被其他强连通分量的牛欢迎
*/

0 条评论

目前还没有评论...