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

来源:互联网 发布:股票交易用什么软件 编辑:程序博客网 时间:2024/05/29 10:32

1051: [HAOI2006]受欢迎的牛

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 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

Sample Output

1

HINT

100%的数据N<=10000,M<=50000

Source

[Submit][Status][Discuss]

【题解】【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;}


0 0
原创粉丝点击