图论连通性合集(持续更新)

强连通分量

定义有向图中的强连通分量为所有点可以相互到达的极大子图.

在一些题目中,可以将强连通分量视为一个点,建出来新图一定是 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;
}

kosaraju

posted @ 2025-06-06 20:00  Ydoc770  阅读(28)  评论(1)    收藏  举报