2359. 【USACO FALL03】受欢迎的牛 (Standard IO)

11 篇文章 0 订阅
2 篇文章 0 订阅

Description

  每头牛都有一个梦想:成为一个群体中最受欢迎的名牛!在一个有N(1<=N<=10,000)头牛的牛群中,给你M(1<=M<=50,000)个二元组(A,B),表示A认为B是受欢迎的。既然受欢迎是可传递的,那么如果A认为B受欢迎,B又认为C受欢迎,则A也会认为C是受欢迎的,哪怕这不是十分明确的规定。
  你的任务是计算被所有其它的牛都喜欢的牛的个数。

Input

  第一行,两个数,N和M。第2~M+1行,每行两个数,A和B,表示A认为B是受欢迎的。

Output

  一个数,被其他所有奶牛认为受欢迎的奶牛头数。

Sample Input

3 3
1 2
2 1
2 3

Sample Output

1

Data Constraint

Hint

【样例说明】
  3号奶牛是唯一被所有其他奶牛认为有名的。

思路

开始还以为这是并查集,但两只牛之间若互相喜欢就互相是对方的父亲,统计答案会出现混乱。
所以我们可以试着将A喜欢B,就连上一条A到B的路径,若是有一个集合的每个点可以到达该集合的其余点(也就是强连通分量)则该集合中的牛互相喜欢(看题意),这样我们就可以用tarjan缩点,由一堆点缩成一个个强连通分量。

可以证明,只要该强连通分量没有出度,则该强连通分量点的数量就是我们的答案

证明

因为我们已经将一个有向图缩成了一个个强连通分量,所以新图中就不会形成环(不然在缩点过程就已经变为一个强连通分量)。若一个强连通分量没有出度,则该强连通分量必然被其余强连通分量指向(除了存在有独立的强连通分量的情况,即该强连通分量不与其余任意一个强连通分量有边相连),因为依据题意,A指向B,B指向C,则A指向C。

证毕

代码

#include<cstdio>
#include<iostream>
#include<vector>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<deque>

#define M 50009
#define N 10009
using namespace std;
struct node
 {
    int to;
    int next;
 }edge[M];

 int re[N];
 int list[N];
 int dfn[N];
 bool visit[N];
 int low[N];
 int p[N];
 int w[N];
 int stack[N];
 int tot,cnt;
 int n,m;
 int x,y;
 int maxx;
 int ans;
 int num;
 int k;

 inline int find(int a)
  {
    if(re[a]==a)return a;
    re[a]=find(a);
    return a;
  }
 inline void tarjan(int x)
  {
    stack[++tot]=x;
    low[x]=dfn[x]=++cnt;
    visit[x]=1;
    for(int i=list[x];i!=0;i=edge[i].next)
     {
        if(!dfn[edge[i].to])
          {
          tarjan(edge[i].to);
          low[x]=min(low[x],low[edge[i].to]);
          }
        else
            if(visit[edge[i].to])
              low[x]=min(low[x],low[edge[i].to]);
       }

     if(dfn[x]==low[x])
      {
        num++;
        do
         {
            re[stack[tot]]=num;
            w[num]++;
             maxx=max(maxx,re[stack[tot]]);
            visit[stack[tot]]=0;
            tot--;
         }while(stack[tot+1]!=x);
        }  
  }
int main()
 {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
     {
        scanf("%d%d",&x,&y);

        edge[i].to=y;
        edge[i].next=list[x];
        list[x]=i;
      }
    for(int i=1;i<=n;i++)
     if(!dfn[i])tarjan(i);

    for(int i=1;i<=n;i++)
     {
        for(int j=list[i];j!=0;j=edge[j].next)
         {
            if(p[re[i]])break;
          if(re[i]!=re[edge[j].to])p[re[i]]++;
         }
        }   

    for(int i=1;i<=num;i++)//存在出度为0的强连通分量若存在两个以上,则必然为0,因为对于一个出度为零的强连通分量,必然有一个强连通分量不指向它(因为后一个强连通分量没有出度)而一个强连通分量至少包含一个点,依据题意,必须所有牛都喜欢一头牛才可以累计答案。
     if(!p[i])
      {
      if(ans)
       {
        printf("0\n");
        return 0;
       }
       ans=w[i];
      }
    printf("%d",ans); 
    return 0;
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值