强联通分量(Strongly Connected Components):有向图的极大强连通子图。
为了搞清楚SCC是怎么回事,我们得先明白有向图中的 4 4 4 种边。
- A. 树枝边:DFS树上的边,即指向未访问过节点的边
- B. 前向边:指向DFS树中子树中节点的边
- C. 后向边:指向DFS树中父亲的边
- D. 横叉边:其他边,即指向DFS树中非子树的边
我们需要维护
2
2
2 个东西
- 一个是 d f n dfn dfn 就是 d f s dfs dfs 序,也是我们常说的时间戳。
- 一个是 l o w low low 就是这个点所能到达子树中的,深度最小的点祖先的dfs序编号。
d f n dfn dfn 处理起来很简单,直接 d f s dfs dfs 的时候编一下号就行了。
l o w low low 的初始值为 d f n dfn dfn。
对于边(u,v)
- 若为树枝边,则用low[v]更新
- 若为前向边,因为指向的点的信息已经通过树枝边传递过来,所以无需更新
- 若为后向边,则用dfn[v]更新
- 若为横叉边,则指向另一个强连通分量,无需更新
代码:
int s[MAXN], stop;
int dfn[MAXN], low[MAXN];
int scccnt, sccnum[MAXN];
int dfscnt;
inline void tarjan(int now){
dfn[now] = low[now] = ++dfscnt;
s[stop++] = now;
for (int i = he[now]; i != 0 ; i = ne[i]){
if (!dfn[ed[i]]) {
tarjan(ed[i]);
low[now] = min(low[now], low[ed[i]]);
} else if(!sccnum[ed[i]]) {
low[now] = min(low[now], dfn[ed[i]]);
}
}
if (dfn[now] == low[now]) {
scccnt++;
do {
sccnum[s[--stop]] = scccnt;
} while(s[stop] != now);
}
}
sccnum这里是维护了一个栈,判断横插边。
例题: