//有向图的强连通分量 //每次调用前手动清空vector<int> G //使用时只更新G完成构图 //scc_cnt从1开始计数 //pre[]表示点在DFS树中的先序时间戳 //lowlink[]表示当前点和后代能追溯到的最早祖先的pre值 //sccno[]表示点所在的双连通分量编号 //vector<int> G保存每个点相邻的下一个点序号 //stack<Edge> S是算法用到的栈 const int MAXV = 21000; vector<int> G[MAXV]; int pre[MAXV], lowlink[MAXV], sccno[MAXV], dfs_clock, scc_cnt; 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 = 0; i < n; i++) if(!pre[i]) dfs(i); }; int in[MAXV], out[MAXV]; int main() { // freopen("in.txt", "r", stdin); int T, n, m, a, b; RI(T); REP(kase, T) { CLR(in, 0); CLR(out, 0); RII(n, m); REP(i, n) G[i].clear(); REP(i, m) { RII(a, b); a--; b--; G[a].push_back(b); } find_scc(n); REP(i, n) { int u = i; REP(j, G[i].size()) { int v = G[i][j]; if (sccno[u] != sccno[v]) { out[sccno[u]]++; in[sccno[v]]++; } } } int a = 0, b = 0; if (scc_cnt != 1) FE(i, 1, scc_cnt) { if (in[i] == 0) a++; if (out[i] == 0) b++; } WI(max(a, b)); } return 0; }
Proving Equivalences,布布扣,bubuko.com
原文:http://blog.csdn.net/wty__/article/details/22395883