【Tarjan算法(O(n+m))】
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define clc(a,b) memset(a,b,sizeof(a))
#define min(a,b) (a>b?b:a)
#define MAXN 10010
int n, pre[MAXN], sccno[MAXN], dfs_clock, scc_cnt;
//sccno[1~n]標記每個點在第幾個SCC中,scc_cnt是SCC的計數,dfs_clock遞歸時間戳
vector<int> g[MAXN];
stack<int> s;
int dfs(int u)
{
int lowu = pre[u] = ++dfs_clock, sz = g[u].size();
s.push(u);
rep(i,0,sz)
{
int v = g[u][i];
if(!pre[v])
{
int lowv = dfs(v);
lowu = min(lowu, lowv);
}
else if(!sccno[v]) lowu = min(lowu, pre[v]);
}
if(lowu == pre[u])//u的子孫最早只能回到u,說明找到一個SCC分量
{
int x;
scc_cnt++;
do{
x = s.top();s.pop();
sccno[x] = scc_cnt;
}while(x != u);
/*可以對這個強連通分量操作*/
}
return lowu;
}
void find_scc()
{
dfs_clock = scc_cnt = 0;
clc(pre,0);
clc(sccno,0);
repe(i,1,n) if(!pre[i]) dfs(i);
}
【Kosaraju算法(O(n+m)常數比Tarjan大)】
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
#define min(a,b) (a>b?b:a)
#define MAXN 10010
int sccno[MAXN], vis[MAXN], scc_cnt, n;
vector<int> g[MAXN], gt[MAXN], s;
//gt[]是g[]的相反邊的圖
void dfs1(int u)//獲取拓撲序
{
vis[u] = true;
int sz = g[u].size();
rep(i,0,sz) if(!vis[g[u][i]]) dfs1(g[u][i]);
s.push_back(u);
}
void dfs2(int u)
{
sccno[u] = scc_cnt;
int sz = gt[u].size();
rep(i,0,sz) if(!sccno[gt[u][i]]) dfs2(gt[u][i]);
}
void find_scc()
{
scc_cnt = 0;
s.clear();
clc(sccno,0);
clc(vis,0);
repe(i,1,n) if(!vis[i]) dfs1(i);
per(i,n-1,0) if(!sccno[s[i]]) scc_cnt++, dfs2(s[i]);
}