这是本题用 Tarjan 的 AC 代码:
#include <iostream>
#include <vector>
#include <stack>
const int N = 1e5 + 9;
int n, m;
int dfn[N], low[N], time_stamp;
int scc[N], sz[N], scc_cnt;
bool instk[N];
int deg[N];
std::stack<int> stk;
std::vector<int> adj[N];
void tarjan(int u) {
dfn[u] = low[u] = ++time_stamp;
stk.emplace(u), instk[u] = true;
for (auto v : adj[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = std::min(low[u], low[v]);
} else if (instk[v]) {
low[u] = std::min(low[u], dfn[v]);
}
}
if (low[u] == dfn[u]) {
scc_cnt++;
int v;
// printf("SCC #%d: ", scc_cnt);
do {
v = stk.top(); stk.pop();
instk[v] = false;
scc[v] = scc_cnt;
sz[scc_cnt]++;
// printf("%d ", v);
} while (v != u);
// puts("");
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
adj[u].emplace_back(v);
}
for (int i = 1; i <= n; i++) {
if (!dfn[i]) tarjan(i);
}
for (int u = 1; u <= n; u++) {
for (auto v : adj[u]) {
int sccu = scc[u], sccv = scc[v];
if (sccu != sccv) {
deg[scc[u]]++;
}
}
}
int ans = 0;
for (int i = 1; i <= scc_cnt; i++) {
if (deg[i] == 0 and ans) {
puts("0");
return 0;
} else if (deg[i] == 0) {
ans = sz[i];
}
}
printf("%d\n", ans);
return 0;
}
既然强连通分量编号的倒序就是缩点后的拓扑排序,那么为什么不可以在统计答案的时候判断一旦 deg[i] > 0 就立刻跳出循环?将源代码 56 行到 63 行的 for 循环替换成如下代码是错误的。
for (int i = 1; i <= scc_cnt; i++) {
if (deg[i]) break;
if (deg[i] == 0 and ans) {
puts("0");
return 0;
}
ans = sz[i];
}