题目大意:有n只牛,给出一个受欢迎的关系,受欢迎的关系是单向的但是可以传递,当然,每只牛都是欢迎自己的。问有多少只牛收到所有牛的欢迎。
思路:首先我们要明确这是一张有向图。然后求出所有的强联通分量,把它们压缩成一个点,之后我们就会得到一棵树。最后统计树上入度为0的点,如果有不止一个入度为0的点,那么肯定是不符合题意的,因为这些入度为0的点不会互相欢迎。注意在这里树上入度为0的点可能并不是真正的一个点,有可能是经过我们压缩的强联通分支。
#include<cstdio>
#include<cstring>
#define MAXN 10005
#define Min(a,b) a<b?a:b
using namespace std;
struct e
{
int v;
int next;
}edge[100005];
int cnt;
int head[MAXN];
void add_edge(int u,int v)
{
edge[cnt].v = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}
int n,m;
int dcnt,low[MAXN],dfn[MAXN];
bool instack[MAXN];
int stack[MAXN],tot;
int cut_e;
int belong[MAXN];
int in[MAXN];
void dfs(int u)
{
low[u] = dfn[u] = ++dcnt;//按访问先后顺序标号
stack[++tot] = u;//u进栈
instack[u] = true;//判断横叉边
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if(dfn[v] == -1)
{
dfs(v);//u,v为父子边
low[u] = Min(low[u],low[v]);
}
else if(instack[v])//u,v是返祖边
{
low[u] = Min(low[u],dfn[v]);
}
}
if(low[u] == dfn[u])//u是连通分量的根
{
cut_e++;//连通分量数加1
do
{
belong[stack[tot]] = cut_e;//记录属于第几个连通分量
instack[stack[tot]] = false;//出栈
tot--;
}while(stack[tot+1] != u);
}
}
int main()
{
while(scanf("%d%d",&n,&m) != EOF)
{
memset(head,-1,sizeof head);
memset(edge,0,sizeof edge);
memset(low,0,sizeof low);
memset(dfn,-1,sizeof dfn);
memset(instack,0,sizeof instack);
memset(stack,0,sizeof stack);
memset(belong,0,sizeof belong);
memset(in,0,sizeof in);
tot = 0;
cnt = dcnt = 0;
cut_e = 0;
for(int i = 1; i <= m; i++)
{
int a,b;
scanf("%d%d",&a,&b);
add_edge(a,b);//有向图,立碑纪念
}
for(int i = 1; i <= n; i++)
if(dfn[i] == -1)
dfs(i);
for(int i = 1; i <= n; i++)//同一个连通分量里的所有点看做一个点
{
for(int j = head[i]; j != -1; j = edge[j].next)
{
int v = edge[j].v;
if(belong[i] != belong[v])//统计入度
{
in[belong[i]]++;
}
}
}
int cnt = 0,cntx;
for(int i = 1; i <= cut_e; i++)//统计入度,只能有一个入度为0的点
if(!in[i])
{
cnt++;
cntx = i;
}
if(cnt == 1)
{
int sum = 0;
for(int i = 1; i <= n; i++)
if(belong[i] == cntx)
sum++;
printf("%d\n",sum);
}
else printf("0\n");
}
}