求有向图的强连通分量。
Tarjan 的核心在于 DFS,在 DFS 过程中,我们会遇到如下 4 种边:
- 树枝边:DFS 过程中经过的边,即 DFS 搜索树上的边。
- 前向边:从祖先节点指向后代节点的非树枝边,我们称为前向边。
- 后向边:从后代节点指向祖先节点的非树枝边,我们称为后向边。
- 横叉边:两端无祖先关系的非树枝边,我们称为横叉边(只会向左)。
定义:
dfn[u]
:结点u
的时间戳。
low[u]
:追溯值:结点u
能回溯到的时间戳最小点。
timestamp
:时间戳。
stk[N], top, in_stk[N]
:栈,存未被处理的点。
id[u]
:u
对应scc编号。
siz[v]
:编号为v
的scc的结点数。
缩点,将原图转换为DAG图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| vector<int> g[N]; int dfn[N], low[N], timestamp; int stk[N], top; bool in_stk[N]; int id[N], scc_cnt, siz[N];
void tarjan(int u){ dfn[u] = low[u] = ++timestamp; stk[++top] = u, in_stk[u] = 1; for(auto &v : g[u]){ if(!dfn[v]){ tarjan(v); low[u] = min(low[u], low[v]); } else if(in_stk[v]) low[u] = min(low[u], dfn[v]); } if(dfn[u] == low[u]){ ++scc_cnt; int y; do{ y = stk[top--]; in_stk[y] = 0; id[y] = scc_cnt; siz[scc_cnt]++; }while(y != u); } }
|