bzoj 1051: [HAOI2006]受欢迎的牛 (Tarjan 缩点)

链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1051

思路: 首先用Tarjan把环缩成点,要想收到所有人的欢迎,那么这个点的出度必为0,且只能有一个出度为0的,如果有多个的话那么没有点会收到所有人欢迎,我们找到这个点,这个点可能是缩完点后的点,我们找下这个点代表的环中点的数量。

实现代码:

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

const int M = 5e4+10;
vector<int>g[M];
stack<int>s;
int dfn[M],low[M],vis[M],in[M],out[M],cnt,num,scc[M];
void Tarjan(int u){
    dfn[u] = low[u] = ++cnt;vis[u] = 1;
    s.push(u);
    for(int i = 0;i < g[u].size();i ++){
        int v = g[u][i];
        if(!vis[v]) Tarjan(v),low[u] = min(low[u],low[v]);
        else low[u] = min(low[u],dfn[v]);
    }
    if(low[u] == dfn[u]){
        num++; int now;
        do{
            now = s.top(); s.pop();
            scc[now] = num;
        } while(!s.empty()&&now!=u);
    }
}

int main()
{
    int n,m,u,v;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= m;i ++){
        scanf("%d%d",&u,&v);
        g[u].push_back(v);
    }
    for(int i = 1;i <= n;i ++){
        if(!vis[i]) Tarjan(i);
    }
    for(int i = 1;i <= n;i ++){
        for(int j = 0;j < g[i].size();j ++){
            int v = g[i][j];
            if(scc[i] != scc[v]){
                out[scc[i]]++;
                in[scc[v]]++;
            }
        }
    }
    int k = 0,ans = 0,tag;
    for(int i = 1;i <= num;i ++){
        if(!out[i]){
            k++; tag = i;
        }
    }
    if(k == 1){
        for(int i = 1;i <= n;i ++)
            if(scc[i] == tag) ans++;
        printf("%d\n",ans);
    }
    else printf("0\n");
    return 0;
}

 

posted @ 2019-03-21 22:26  冥想选手  阅读(...)  评论(... 编辑 收藏