图论连通性合集(持续更新)
强连通分量
定义有向图中的强连通分量为所有点可以相互到达的极大子图.
在一些题目中,可以将强连通分量视为一个点,建出来新图一定是 DAG(有向无环图).
tarjan
tarjan 求强连通分量是最广为人知也最常用的,其核心在于对每个点 \(u\),维护 dfs 序 \(dfn_u\) 和 \(low_u\).
在 dfs 过程中,维护一个栈,把所有 dfs 访问到且未处理的点压入栈中.
定义 \(low_u\) 为在 \(u\) 子树中和子树中经过一条非树边可以回溯到的最早节点,也就是其中 \(dfn_v\) 最小的节点.
根据定义,当 \(low_u=dfn_u\) 时说明在处理完子树 \(u\) 后,栈中在其上方未弹出的所有节点和 \(u\) 属于同一个强连通分量,因为其它点 \(u\) 不可达,而子树内属于更小强连通分量的之前已经处理过并弹出栈了.
所以在 dfs 过程中:
- 如果遇到已经入栈的节点 \(v\),说明之前访问过,也就说明这条边为非树边,\(v\) 是 \(u\) 的祖先,考虑用 \(dfn_v\) 更新 \(low_u\).
- 如果遇到没入栈的节点 \(v\) 就一定是在 \(u\) 子树内的,于是先接着处理子树 \(v\). 回溯到 \(u\) 时考虑用 \(low_v\) 更新 \(low_u\),因为 \(v\) 可能到达 \(dfn\) 更小的点.
\(low_u\) 的维护实际上是一个树上 DP 的过程,可以自己多想想,加深理解.
点击查看代码
int cnts, bl[maxn];
int cnt, dfn[maxn], low[maxn]; bool ins[maxn]; stack<int> s;
void tarjan(int u) {
dfn[u] = low[u] = cnt++, s.push(u), ins[u] = true;
for(int i = h[u], v; i; i = e[i].nxt) {
v = e[i].v;
if(!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
else if(ins[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u]) {
cnts++;
for(int v = 0; v != u; ins[v] = false) v = s.top(), s.pop(), bl[v] = cnts;
} return;
}

浙公网安备 33010602011771号