题目类型 强连通分量
题目意思
给出 n (n<=10000) 个点和 m(<=50000) 条有向边 问有多少个点满足 其他点到这个点都可达
解题方法
求出强连通分量后 把在同一个强连通分量中的点看成一个点(即缩成一个点) 这时点与点之间还可能存在有向边(其实就是桥)
那么要达成题目的要求 则出度为 0 的点只能有一个 而这个点对应的强连通分量包含的点数即题目所求
参考代码 - 有疑问的地方在下方留言 看到会尽快回复的
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
using namespace std;
const int maxn = 1e4 + 10;
vector<int>G[maxn];
int pre[maxn], lowlink[maxn], sccno[maxn], dfs_clock, scc_cnt;
int d[maxn];
stack<int>S;
void dfs(int u) {
pre[u] = lowlink[u] = ++dfs_clock;
S.push(u);
for( int i=0; i<G[u].size(); i++ ) {
int v = G[u][i];
if(!pre[v]) {
dfs(v);
lowlink[u] = min(lowlink[u], lowlink[v]);
}
else if(!sccno[v]) {
lowlink[u] = min(lowlink[u], pre[v]);
}
}
if(lowlink[u] == pre[u]) {
scc_cnt++;
for(;;) {
int x = S.top(); S.pop();
sccno[x] = scc_cnt;
if(x == u) break;
}
}
}
void find_scc(int n) {
dfs_clock = scc_cnt = 0;
memset(sccno, 0, sizeof(sccno));
memset(pre, 0, sizeof(pre));
for( int i=1; i<=n; i++ ) {
if(!pre[i]) dfs(i);
}
}
int main() {
freopen("in", "r", stdin);
int n, m;
while(scanf("%d%d", &n, &m) != EOF) {
for( int i=1; i<=n; i++ ) G[i].clear();
for( int i=0; i<m; i++ ) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
}
find_scc(n);
memset(d, 0, sizeof(d));
for( int i=1; i<=n; i++ ) {
for( int j=0; j<G[i].size(); j++ ) {
int v = G[i][j];
if(sccno[i] != sccno[v]) {
d[sccno[i]]++;
}
}
}
int ans = 0, nans = 1;
for( int i=1; i<=scc_cnt; i++ ) {
if(d[i] == 0) {
ans++;
nans = i;
}
}
if(ans > 1) printf("0\n");
else {
ans = 0;
for( int i=1; i<=n; i++ ) if(sccno[i] == nans) ans++;
printf("%d\n", ans);
}
}
return 0;
}