题目地址:
https://www.luogu.com.cn/problem/P2341
题目描述:
每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果
A
A
A喜欢
B
B
B,
B
B
B喜欢
C
C
C,那么
A
A
A也喜欢
C
C
C。牛栏里共有
N
N
N头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。
输入格式:
第一行:两个用空格分开的整数:
N
N
N和
M
M
M。
接下来
M
M
M行:每行两个用空格分开的整数:
A
A
A和
B
B
B,表示
A
A
A喜欢
B
B
B。
输出格式:
一行单独一个整数,表示明星奶牛的数量。
数据范围:
对于
10
%
10\%
10%的数据,
N
≤
20
N\le20
N≤20,
M
≤
50
M\le50
M≤50。
对于
30
%
30\%
30%的数据,
N
≤
1
0
3
N\le10^3
N≤103,
M
≤
2
×
1
0
4
M\le2\times 10^4
M≤2×104。
对于
70
%
70\%
70%的数据,
N
≤
5
×
1
0
3
N\le5\times 10^3
N≤5×103,
M
≤
5
×
1
0
4
M\le5\times 10^4
M≤5×104。
对于
100
%
100\%
100%的数据,
1
≤
N
≤
1
0
4
1\le N\le10^4
1≤N≤104,
1
≤
M
≤
5
×
1
0
4
1\le M\le5\times 10^4
1≤M≤5×104。
可以用Tarjan算法先对图缩点,这样就可以看缩点后的出度为 0 0 0的点有多少个,如果不唯一,则没有奶牛可以当明星。否则看一下出度为 0 0 0的点的size即可。思路参考https://blog.csdn.net/qq_46105170/article/details/116681582。代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e4 + 10, M = 5e4 + 10;
int n, m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp;
int stk[N], top;
bool in_stk[N];
int id[N], scc_cnt, sz[N];
int dout[N];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void tarjan(int u) {
dfn[u] = low[u] = ++timestamp;
stk[top++] = u, in_stk[u] = true;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (in_stk[v]) low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
scc_cnt++;
int y;
do {
y = stk[--top];
in_stk[y] = false;
id[y] = scc_cnt;
sz[scc_cnt]++;
} while (y != u);
}
}
int main() {
memset(h, -1, sizeof h);
scanf("%d%d", &n, &m);
while (m--) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
}
for (int i = 1; i <= n; i++)
if (!dfn[i]) tarjan(i);
for (int i = 1; i <= n; i++)
for (int j = h[i]; ~j; j = ne[j])
if (id[i] != id[e[j]])
dout[id[i]]++;
int zero = 0, res;
for (int i = 1; i <= scc_cnt; i++) {
if (!dout[i]) {
if (++zero > 1) {
res = 0;
break;
}
res = sz[i];
}
}
printf("%d\n", res);
}
时间复杂度 O ( N + M ) O(N+M) O(N+M),空间 O ( N ) O(N) O(N)。