每一头牛的愿望就是变成一头最受欢迎的牛。现在有 $N$ 头牛,给你 $M$ 对整数 $(A,B)$,表示牛 $A$ 认为牛 $B$ 受欢迎。这种关系是具有传递性的,如果 $A$ 认为 $B$ 受欢迎,$B$ 认为 $C$ 受欢迎,那么牛 $A$ 也认为牛 $C$ 受欢迎。你的任务是求出有多少头牛被除自己之外的所有牛认为是受欢迎的。
链接
题解
用 Tarjan 算法找出图中每一个强连通分量,缩点,然后转化为有向无环图。
在一个强连通分量里的牛互相认为彼此是受欢迎的,由于关系是具有传递性的,只要其它强连通分量都有某头牛认为这个强连通分量的某头牛是受欢迎的,那么这个强连通分量的牛都是受欢迎的。
对于已经完成缩点后的图:
- 如果某个点存在出边,则出边指向的点不可能指向自身,因为这是无环图。
- 如果存在两个或两个以上的点没有出边,则这样的点均无法指向其它这样的点。
所以,对于缩点后的图,只能有一个点被其它点指向或间接指向,否则输出 0。
代码
scc[u]
表示 $u$ 所在强连通分量的序号;sccSize[i]
表示序号为 $i$ 的强连通分量中,节点的数目;sccTot
表示强连通分量的个数。
out[i]
表示序号为 $i$ 的强连通分量出边数目(出度)。
#include <cstdio>
#include <cstring>
const int vSIZE = 100005;
const int eSIZE = 400005;
int out[vSIZE];
int scc[vSIZE], sccSize[vSIZE], sccTot;
int st[vSIZE], top;
int low[vSIZE], dfn[vSIZE], time;
int h[eSIZE], to[eSIZE], nxt[eSIZE], tot;
int min(int x, int y) {
return x < y ? x : y;
}
void add(int x, int y) {
to[++tot] = y;
nxt[tot] = h[x];
h[x] = tot;
}
void tarjan(int x) {
low[x] = dfn[x] = ++time;
st[++top] = x;
for (int i = h[x]; i; i = nxt[i]) {
int y = to[i];
if (!dfn[y]) {
tarjan(y);
low[x] = min(low[x], low[y]);
} else if (!scc[y]) {
low[x] = min(low[x], dfn[y]);
}
}
if (low[x] == dfn[x]) {
scc[x] = ++sccTot;
sccSize[sccTot] ++;
while (st[top] != x) {
scc[st[top--]] = sccTot;
sccSize[sccTot] ++;
}
top--;
}
}
int main() {
int n, m, ans = 0;
scanf("%d %d", &n, &m);
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d %d", &x, &y);
add(x, y);
}
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 = nxt[j]) {
// 两个点所属的强连通分量不同
if (scc[i] != scc[to[j]]) out[scc[i]]++;
}
}
for (int i = 1; i <= sccTot; i++) {
// 如果没有出边
if (!out[i]) {
if (!ans) ans = sccSize[i]; // 这个强连通分量的牛都是受欢迎的
else ans = 0; // 已经存在点没有出边的情况
}
}
printf("%d
", ans);
return 0;
}