1051: [HAOI2006]受欢迎的牛
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4091 Solved: 2194
[ Submit][ Status][ Discuss]
Description
每一头牛的愿望就是变成一头最受欢迎的牛。现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎。 这
种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认为牛C受欢迎。你的任务是求出有多少头
牛被所有的牛认为是受欢迎的。
Input
第一行两个数N,M。 接下来M行,每行两个数A,B,意思是A认为B是受欢迎的(给出的信息有可能重复,即有可
能出现多个A,B)
Output
一个数,即有多少头牛被所有的牛认为是受欢迎的。
Sample Input
3 3
1 2
2 1
2 3
1 2
2 1
2 3
Sample Output
1
HINT
100%的数据N<=10000,M<=50000
Source
【题解】【Tarjan缩点,一个受欢迎的牛必定与每个点都连边,所以,缩点,把在一个强连通分量的点缩为一个,然后扫描每一条边,最后找出度为0的点】
(附:了tarjan算法的基础,这道题就可以搞了,如果存在受所有牛都欢迎的牛,那么这些牛不可能独立存在,他们要么在一个强联通块中,要么只有一头牛。那么我们可以把所有强连通块用tarjan算法处理出来,同时计算每个强连通块中点的个数。然后枚举所以的边,如果一条边的两个结点属于不同的强连通块,那么就把发出这条边的联通块的出度+1,最后寻找是否存在出度为0的强连通块,因为受所有牛的欢迎(在一个强联通块中的牛只要有一个欢迎另一头不再强连通块中的牛,那么强连通块中其他牛就欢迎)所以一定所以的强连通块都有边连向这个强联通块。)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[500010],nxt[500010],p[500010],tot;
int dis[500010],dft[500010],root,cnt;
int x[500010],y[500010];
int d[500010],top;
int f[500010],size[500010],num[500010];
int n,m;
bool vis[500010];
inline int tmp(int a,int b)
{
return a>b;
}
inline void add(int x,int y)
{
tot++; a[tot]=y; nxt[tot]=p[x]; p[x]=tot;
}
void tarjan(int u)
{
dis[u]=dft[u]=++cnt;
d[++top]=u;
vis[u]=1;
int v=p[u];
while(v>=0)
{
if(!dft[a[v]])
{
tarjan(a[v]);
dis[u]=min(dis[u],dis[a[v]]);
}
else
if(vis[a[v]]&&dis[u]>dft[a[v]]) dis[u]=dft[a[v]];
v=nxt[v];
}
int b;
if(dis[u]==dft[u])
{
++root;
do{
b=d[top--];
size[root]++;
f[b]=root;
vis[b]=0;
}while(u!=b);
}
return;
}
inline void slove()
{
cnt=top=root=0;
memset(dis,0,sizeof(dis));
memset(dft,0,sizeof(dft));
for(int i=1;i<=n;++i)
if(!dft[i]) tarjan(i);
return;
}
inline void count()
{
for(int i=1;i<=m;++i)
if(f[x[i]]!=f[y[i]])
num[f[x[i]]]++;
return;
}
int main()
{
int i,j;
memset(p,-1,sizeof(p));
memset(nxt,-1,sizeof(nxt));
memset(num,0,sizeof(num));
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i)
{
scanf("%d%d",&x[i],&y[i]);
add(x[i],y[i]);
}//不知道为什么,如果每条边只存一遍,就会WA(重复的边也要存储)
slove();
count();
int ans=0;
for(i=1;i<=root;++i)
if(!num[i])
{
if(ans)
{printf("0\n"); return 0;}
ans=i;
}
printf("%d\n",size[ans]);
return 0;
}