洛谷P2341 受欢迎的牛 Tarjan缩点

题目链接:https://www.luogu.org/problem/P2341

题意:A喜欢B,B喜欢C,那么A就喜欢C,每个人都必定喜欢自己,求问被所有人喜欢的人有多少个

输入格式:n,m分别是人总数和喜欢关系的数目,接下来m行每行两个数字a,b代表a喜欢b

分析:输入格式方面很容易想到可以转化成图论题来做,在图上,A喜欢B就代表A可达B,强连通分量里面所有点是互相可达的,而强连通分量a只要有一个点可达强连通分量b,那么a中所有点都可达b,我们便利用Tarjan算法算出强连通分量并且记录每个强连通分量里点的个数,然后进行缩点。缩点后我们继续观察,发现如果出度为0的点只有一个,那么这肯定是个连通图,并且其他所有点可以达到该点,而该点无法到达其他点,那么这个点(缩点后的强连通分量)内点的个数就是答案,而若有两个以上的点出度为0,该图则不是联通图,答案为0.

直接套Tarjan算法缩点模板,记录数目并最后记录每个点出度即可即可。

#include<bits/stdc++.h>
#define maxn 10001
using namespace std;
vector<int>G[maxn];
stack<int>s;
int n,m;
int dfn[maxn],vis[maxn],low[maxn],color[maxn],num[maxn],colornum=0,res[maxn],cnt;
//num记录每个颜色下点的数目,res记录每个点的出度 
void paint(int x)
{
    s.pop();
    color[x]=colornum;
    num[colornum]++;
    vis[x]=false;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++cnt;
    s.push(x);
    vis[x]=true;
    for(int i=0;i<G[x].size();i++)
    {
        int q=G[x][i];
        if (!dfn[q])
        {
            tarjan(q);
            low[x]=min(low[x],low[q]);
        }
        else if (vis[q]) low[x]=min(low[x],dfn[q]);
    }
    if (low[x]==dfn[x])
    {
        colornum++;
        while(s.top()!=x)
        {
            int t=s.top();
            paint(t);
        }
        paint(x);
    }
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int u,v;
        cin>>u>>v;
        G[u].push_back(v);
    }
    for(int i=1;i<=n;i++)
    {
        if (!dfn[i]) tarjan(i);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<G[i].size();j++){
            if(color[i]!=color[G[i][j]]){
                res[color[i]]++;
            }
        }
    }
    for(int i=1;i<=colornum;i++){
        if(ans==0&&res[i]==0){
            ans=num[i];
        }
        else if(res[i]==0){
            ans=0;break;
        }
    }
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/qingjiuling/p/11313561.html
今日推荐