tarjan强连通分量 T3 模板题 P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自

恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 A 喜欢 B,B 喜欢 C,那么 A 也喜欢 C。牛栏里共有 N 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

第一行:两个用空格分开的整数:N 和 M。

接下来 M 行:每行两个用空格分开的整数:A 和 B,表示 A 喜欢 B。

input

3 3
1 2
2 1
2 3

output

1

思路

如果是暴力做法, 对于每一个点, 遍历其他所有点, 是否满足都对该点有一条边, 来判断。时间复杂度很高。

可以用 tarjan 缩点成为一个拓扑图: a->b->c->d->e 显然需要关心最后一个节点, 该点没有子节点, 那么对于它来说, 前面所有点都能到达该点, 该点就是最受欢迎的。 但如果 还有一条边为 d->f 即有两个出度为0的点, 那么就不存在最受欢迎的点。

总思路是:tarjan求强连通分量并缩点成为拓扑图, 同时计算出度, 遍历所有强连通分量根节点, 找出度为0的点数量, 若为1则加上该强连通分量的点数量, 若大于1则输出0。

代码

const int N = 1e5 + 10;
 
int h[N], e[N], ne[N], idx;
int dfs[N], low[N];
int stk[N], top;
int id[N], timestamp, scc_cnt;
int dout[N], scc_size[N];
bool in_stk[N];
int n, m;
 
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
 
void tarjan(int u)
{
    dfs[u] = low[u] = ++timestamp;
    stk[++top] = u, in_stk[u] = true;
 
    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if (!dfs[j])
        {
            tarjan(j);
            low[u] = min(low[u], low[j]);
        }
        else if (in_stk[j])
            low[u] = min(low[u], dfs[j]);
    }
 
    if (low[u] == dfs[u])
    {
        ++scc_cnt;
        int y;
        do
        {
            y = stk[top--];
            in_stk[y] = false;
            id[y] = scc_cnt;
            scc_size[scc_cnt]++;
 
        } while (y != u);
    }
}
 
int main()
{
    FasterIO;
    mem(h, -1);
    cin >> n >> m;
    while (m--)
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
    }
 
    for (int i = 1; i <= n; i++)
        if (!dfs[i])
            tarjan(i);
 
    for (int i = 1; i <= n; i++)
    {
        for (int j = h[i]; ~j; j = ne[j])
        {
            int k = e[j];
            int a = id[i], b = id[k];
            if (a != b)
            {
                dout[a]++;
            }
        }
    }
 
    int zeros = 0, sum = 0;
    for (int i = 1; i <= scc_cnt; i++)
        if (!dout[i])
        {
            zeros++;
            sum += scc_size[i];
            if (zeros > 1)
            {
                sum = 0;
                break;
            }
        }
    cout << sum << endl;
    return 0;
}