有向图的强连通分量
1.Tarjan
O(V+E)
 dfn[u]表示dfs时达到顶点u的次序号(时间戳),low[u]表示以u为根节点的dfs树中次序号最小的顶点的次序号,所以当dfn[u]=low[u]时,以u为根的搜索子树上所有节点是一个强连通分量。 先将顶点u入栈,dfn[u]=low[u]=++idx,扫描u能到达的顶点v,如果v没有被访问过,则dfs(v),low[u]=min(low[u],low[v]),如果v在栈里,low[u]=min(low[u],dfn[v]),扫描完v以后,如果dfn[u]=low[u],则将u及其以上顶点出栈。
# include<bits/stdc++.h>
using namespace std;
const int MAXN=2e4+100;
vector<int> G[MAXN];
stack<int> S;
int dfn[MAXN],low[MAXN],sccno[MAXN],dfs_clock,scc_cnt;
void dfs(int u)
{
    dfn[u]=low[u]=++dfs_clock;
    S.push(u);
    int len=G[u].size();
    for(int i=0;i<len;++i){
        int v=G[u][i];
        if(!dfn[v]){
            dfs(v);
            low[u]=min(low[u],low[v]);
        }else if(!sccno[v]){
            low[u]=min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[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(dfn,0,sizeof(dfn));
    for(int i=1;i<=n;++i){
        if(!dfn[i]) dfs(i);
    }
}
int scc_du[MAXN];
queue<int> Qu;
int main()
{
    int n,m; scanf("%d%d",&n,&m);
    while(m--){
        int u,v; scanf("%d%d",&u,&v);
        G[u].push_back(v);
    }
    find_scc(n);
    for(int i