强连通分量

定义:

强连通指的是对于一个有向图,每个点都有路径到另外一个点。

强连通分量则指的是对于一个图,它的极大强连通子图。

tanjan 求法:

对于一个图,考虑他的 dfs 生成树(即为对原图进行 dfs 的一棵树)。

那么对于这棵树,搜索时会出现四种边:

树枝边:搜索到没被访问过的节点,且在树中当前节点的直接儿子

前向边:搜索到没被访问的节点,但在树中当前节点的间接儿子

横叉边:搜索到已访问到的节点,但不是当前节点的祖先

返祖边:搜索到已访问的节点,且当前节点的祖先


性质: 如果 \(u\) 是一个强连通分量中的第一个访问到的节点,那么其余节点一定是在搜索树中 \(u\) 的子树中。

证明: 反证法即可。


那么现在我们考虑到达 \(u\) 节点的时间戳,即为 \(u\) 是第几个到达的。这里记作 \(dfn[u]\).

在回溯的过程中,维护一个栈,用于存储待处理的节点。

再考虑在以 \(u\) 为根的子树中能到达的最早的在栈中的节点的时间戳。这里记作 \(low[i]\).

那么对于没被访问的节点,有 \(low[u]=\min(low[u],low[v])\)

对于访问过的且在栈中的节点,有 \(low[u]=\min(low[u],dfn[v])\)

其他情况对 \(low[u]\) 均无贡献。

那么当 \(low[u]=dfn[u]\) 时,就表示所有在以 \(u\) 为根的子树中 \(low\) 值等于 \(low[u]\) 的节点组成的图已经满足了极大性和强连通性

且因为以 \(u\) 为根的子树中 \(low\) 值不等于 \(low[u]\) 的节点已经处理完了,所以这个栈中在 \(u\) 上的节点的 \(low\) 值都等于 \(low[u]\) ,此时他们就组成了一个强连通分量

代码:

void tarjan(int u){
	dfn[u]=low[u]=++dfnnow;
	vis[u]=true;
	st[++top]=u;
	in_st[u]=true;
	for(int i=head[u];i;i=edges[i].next){
		int v=edges[i].v;
		if(!vis[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(in_st[v]){
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(dfn[u]==low[u]){
		scc++;
		while(st[top]!=u){
			siz[scc]++;
			in_st[st[top]]=false;
			col[st[top]]=scc;
			top--;
		}
		siz[scc]++;
		in_st[st[top]]=false;
		col[st[top]]=scc;
		top--;
	}
	return;
}
posted @ 2024-04-25 13:00  Little_corn  阅读(79)  评论(0编辑  收藏  举报