【强连通分量缩点 经典好题】POJ - 2186 A - Popular Cows

A - Popular Cows  POJ - 2186 

Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is 
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow. 

Input

* Line 1: Two space-separated integers, N and M 

* Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular. 

Output

* Line 1: A single integer that is the number of cows who are considered popular by every other cow. 

Sample Input

3 3
1 2
2 1
2 3

Sample Output

1

Hint

Cow 3 is the only cow of high popularity. 

给你n个点,m条有向边,问你有多少个点是能从任意点走到的
首先正常强连通缩点,每一个点的强联通标号都存在belong里
遍历每一条边,如果是属于同一个强连通里的,就不管,否则就建边
会得到一个有向无环图,记录此图中所有的点的出度,如果这个图里
有超过两个出度为零的点,就不是连通图,答案为0
否则,就输出度为零的强连通分量里的点数

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn=1e4+10;
vector <int> G[maxn];
int dfn[maxn],low[maxn],s[maxn];
int belong[maxn],vis[maxn],out[maxn];
int cnt,num,top;

void dfs(int u)
{
    dfn[u]=low[u]=++num;
    s[++top]=u;
    vis[u]=1;
    for(int i=0; i<G[u].size(); i++)
    {
        int v=G[u][i];
        if(!dfn[v])
        {
            dfs(v);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v]&&dfn[v]<low[u])
            low[u]=dfn[v];
    }
    if(low[u]==dfn[u])
    {
        cnt++;
        int v;
        do
        {
            v=s[top--];
            vis[v]=0;
            belong[v]=cnt;
        } while(v!=u);
    }
}

void tarjan(int n)
{
    cnt=num=top=0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(belong,0,sizeof(belong));
    memset(out,0,sizeof(out));
    for(int i=1; i<=n; i++)
    {
        if(!dfn[i]) dfs(i);
    }
}

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1; i<=m; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
        }
        tarjan(n);
        for(int u=1; u<=n; u++)
        {
            for(int i=0; i<G[u].size(); i++)
            {
                int v=G[u][i];
                if(belong[u]==belong[v]) continue;  //所点后要是同一个强联通分量里的就不管,不是的建边
                else out[belong[u]]++;
            }
        }
        int sum=0,flag;
        for(int i=1; i<=cnt; i++)
        {
            if(out[i]==0) 
            {
                sum++;
                flag=i;
            }
        }
        if(sum!=1) printf("0\n");
        else
        {
            int ans=0;
            for(int i=1; i<=n; i++)
            {
                if(belong[i]==flag) ans++;
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}




 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值