思路
欢迎的牛有两种情况:
-
子图是一个强连通图,里面的每个结点都满足条件
-
出度为0的结点,满足情况
综合上面两种情况,将图缩点后,就统计出度为0的结点。如果缩点后的图有多个出度为0的,则不符合题意提前退出。
AC代码
/**
缩点 + 统计出度为零的点是否等于1
*/
#include<cstdio>
#include<iostream>
#include<stack>
#include<cstring>
#include<algorithm>
using namespace std;
#define IOS ios::sync_with_stdio(false)
const int maxm = 5e4 + 5;
const int maxn = 1e4 + 5;
struct Edge{
int to, next;
}edge[maxm];
// dfn为时间戳数组,low代表最小时间戳数组,color对一个强连通分量染同色,all一个强连通分量的个数。
int head[maxn], dfn[maxn], low[maxn], all[maxn], color[maxn], outd[maxn], tot, cnt, ind = 1;
bool instack[maxn];
stack<int> st;
void add(int u, int v){
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}
void tarjan(int u){
dfn[u] = low[u] = ind++;
st.push(u);
instack[u] = true;
for (int i = head[u]; ~i; i = edge[i].next){
int v = edge[i].to;
if (dfn[v] == 0){
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (instack[v]){
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]){
tot++;
int now;
do{
now = st.top(); st.pop();
instack[now] = false;
color[now] = tot;
all[tot]++;
} while(now != u);
}
}
void solve(){
int n, m, u, v;
scanf("%d%d", &n, &m);
memset(head, -1, sizeof(head));
while (m--){
scanf("%d%d", &u, &v);
add(u, v);
}
for (int i = 1;i <= n; ++i){
if (dfn[i] == 0){
tarjan(i);
}
}
for (int u = 1; u <= n; ++u){
for (int i = head[u]; ~i; i = edge[i].next){
int v = edge[i].to;
//统计缩点后的出度情况
if (color[u] != color[v]){
outd[color[u]]++;
}
}
}
int pos = 0;
//对缩点后进行遍历
for (int i = 1; i <= tot; ++i){
if (outd[i] == 0){
if (pos != 0){
puts("0");
return;
}
pos = i;
}
}
printf("%d\n", all[pos]);
}
int main(){
solve();
return 0;
}