洛谷P2341「HAOI2006」题解

LuoguP2341 「HAOI2006」受欢迎的牛

题目描述

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶

牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果A喜

欢B,B喜欢C,那么A也喜欢C。牛栏里共有N 头奶牛,给定一些奶牛之间的爱慕关系,请你

算出有多少头奶牛可以当明星。

输入输出格式

输入格式

第一行:两个用空格分开的整数:N和M

第二行到第M + 1行:每行两个用空格分开的整数:A和B,表示A喜欢B

输出格式

第一行:单独一个整数,表示明星奶牛的数量

INPUT & OUTPUT’s examples

Input’s eg #1

1
2
3
4
3 3
1 2
2 1
2 3

Output’s eg #1

1
1

说明

只有 3 号奶牛可以做明星

【数据范围】

10%的数据N<=20, M<=50

30%的数据N<=1000,M<=20000

70%的数据N<=5000,M<=50000

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

分析

我们把每一对爱慕关系连成一条有向边。

然后通过分析题意可以得到,受欢迎的奶牛只可能是图中唯一出度为$0$的强连通分量中的所有节点(奶牛)。

所以,在图中如果出现两个或者以上的出度为$0$的强连通分量,则不存在明星奶牛,直接输出$0$即可。

所以利用Tarjan算法求出强连通分量,并把每一个强连通分量内的节点染成同一种颜色。

然后Tarjan缩点,把每一个强连通分量缩成一个节点。

最后遍历每一个强连通分量,求出这个强连通分量的出度,如果有两个出度为$0$的强连通分量则输出$0$。

否则,用一个变量记录一下出度为$0$的强连通分量的编号,输出其元素个数即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/* Headers */
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cctype>
#include<stack>
#include<algorithm>
/* definitions */
const int MAXN = 5e4+10;
struct Edge{
int next,to;
};
Edge Graph[MAXN];
int n,m,dfn[MAXN],low[MAXN],head[MAXN],cnt;
int indexs,Belong[MAXN],out[MAXN],all[MAXN];
std::stack<int> s;
int count;
bool visited[MAXN];
/* functions */
inline void Add_Edge(int from,int to){
Graph[++cnt].next=head[from];
Graph[cnt].to=to;
head[from]=cnt;
}
inline void Tarjan(int k){
int first;
dfn[k]=low[k]=++indexs;
s.push(k);
visited[k]=true;
for(int i=head[k];i;i=Graph[i].next){
int u=Graph[i].to;
if(!dfn[u]){
Tarjan(u);
low[k]=std::min(low[k],low[u]);
}
else if(visited[u])low[k]=std::min(low[k],dfn[u]);
}
if(low[k]==dfn[k]){
++count;
do{
first=s.top();
s.pop();
visited[first]=false;
Belong[first]=count;
all[count]++;
}while(k!=first);
}
}
int main(int argc,char *argv[]){
scanf("%d%d",&n,&m);
for(register int i=1;i<=m;i++){
int f,t;
scanf("%d%d",&f,&t);
Add_Edge(f,t);
}
for(register int i=1;i<=n;i++){
if(!dfn[i])Tarjan(i);
}
for(register int i=1;i<=n;i++){
for(int j=head[i];j;j=Graph[j].next){
int u=Graph[j].to;
if(Belong[i]!=Belong[u]){
out[Belong[i]]++;
}
}
}
int tot=0;
for(register int i=1;i<=count;i++){
if(!out[i]){
if(tot){puts("0");return 0;}
tot=i;
}
}
printf("%d\n",all[tot]);
return 0;
}

THE END