题目描述
每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 AA 喜欢 BB,BB 喜欢 CC,那么 AA 也喜欢 CC。牛栏里共有 NN 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。
输入格式
第一行:两个用空格分开的整数:NN 和 MM。
接下来 MM 行:每行两个用空格分开的整数:AA 和 BB,表示 AA 喜欢 BB。
输出格式
一行单独一个整数,表示明星奶牛的数量。
输入输出样例
输入 #1复制
3 3
1 2
2 1
2 3
输出 #1复制
1
说明/提示
只有 33 号奶牛可以做明星。
解:
首先,不难发现,如果这所有的牛都存在同一个强联通分量里。那么它们一定互相受欢迎。
那么,我们怎么来找明星呢。
很简单,找出度为00的强联通分量中的点。这样可以保证所有的人都喜欢它,但是它不喜欢任何人,所以说不存在还有人事明星。
此题还有一个特殊情况:
如果有两个点分别满足出度为零的条件,则没有明星,这样无法满足所有的牛喜欢他。
#include <iostream>
#include<cstdio>
#include<stack>
using namespace std;
#define N 10005
#define M 50005
int n,m,cnt=1,head[N],dfn[N],book[N],low[N],num,belong[N],de[N],numm[N];
struct e{
int to;
int nxt;
};
e edge[M];
void add(int s, int v){
edge[cnt].to = v;
edge[cnt].nxt = head[s];
head[s] = cnt++;
return;
}
int t=0;
stack <int> zhan;
void tarjan(int x){
low[x] = dfn[x] = ++t;
zhan.push(x);
book[x] = 1;
for(int i=head[x]; i ;i = edge[i].nxt){
if(dfn[edge[i].to]==0){//不能写成book[] == 0 因为只要搜过了就不用再搜了,下方样例可以很好说明 !!!
tarjan(edge[i].to);
low[x] = min(low[x], low[edge[i].to]);
}
else if(book[edge[i].to] == 1){
low[x] = min(low[x], dfn[edge[i].to]);
}
}
if(dfn[x] == low[x]){
num++;
while(1){
int a = zhan.top();
belong[a] = num;//每一个点属于哪个强连通区域
zhan.pop();
book[a] = 0;
if(a == x) break;
}
}
return ;
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
}
for(int i=1;i<=n;i++){
if(dfn[i] == 0) tarjan(i);
}
for(int i=1;i<=n;i++){
numm[belong[i]]++;
for(int j=head[i];j;j=edge[j].nxt){
if(belong[i]!=belong[edge[j].to]){
de[belong[i]]++;//记录每组连通分量的出度
}
}
}
int rec;
int flag=0;
for(int i=1;i<=num;i++){//遍历所有的连通区域
if(de[i]==0){
rec = i;
flag++;
}
}
if(flag>1) printf("0\n");//出现一次以上出度为零的连通分量
else
printf("%d\n",numm[rec]);
return 0;
}
/*
6 8
1 3
1 2
3 4
2 4
4 1
3 5
4 6
5 6
*/