「学习笔记」强联通分量

一.强连通分量的相关概念#

  • 强连通图#

    在一个有向图中,存在一条路径,使得所有的节点都被经过至少一次,那么这样的图称作强连通图。下图就是一个强连通图:
    image

  • 强联通分量#

    在强联通图的基础上加入一些点和路径,使得现在的图不再强联通,那么原来强联通的部分称作强连通分量。下图绿色部分就是强联通分量:
    image

二.强联通分量的作用#

在解决图论问题时,我们可以利用强联通分量的知识,将图中的各个强连通分量都缩成一个点,便于解决问题。有时,通过求强连通分量,可以得出图中环及环的长度。

三.Tarjan 算法求强联通分量#

1. 基本思路#

先考虑一下强连通分量的性质:存在一条路径可以从任意一点出发再到达该点。

在查找过程中,可以对经过的点标记。发现节点 u 连向的节点 v 被标记过,那么就说明找到了一条路径,这条路径上的所有节点构成一个强联通分量。为了保存这条路径上的节点,最合适的数据结构就是栈。找到路径时,逐个弹出栈中的元素,直到起始节点。再继续搜索其它强连通分量。

2. 算法讲解#

需要对每一个节点 u 创建以下两个变量:
dfnu: 表示 DFS 时节点 u 被搜索到的次序。

lowu: 表示节点 u 能够回溯到的最早的在栈中的节点。用定义来解释:设以节点 u 为根的子树为 subtreeu,那么 lowu=min(dfnv) (vsubtreeu)

然后我们就能轻易得出三个性质:

  • 1.u 为根节点的子树中的任意一个节点的 dfn 值都小于 dfnu

  • 2. 从根出发的一条路径上的 dfn 值严格递增。

  • 3. 从根出发的一条路径上的 low 值严格非降。

接下来 DFS 图中的所有节点。

搜索过程中,对于相邻的节点 uv,考虑以下三种情况:

  • 1. 节点 v 未被访问过:继续对节点 v 进行 DFS。回溯过程,用 lowv 更新 lowu。因为存在节点 uv 的直接路径,那么节点 u 能回溯到栈中的节点,节点 v 必然也能回溯到。

  • 2. 节点 v 被访问过,已经在栈中:用 dfnv 来更新 lowu

  • 3. 节点 v 被访问过,但不在栈中:说明 v 被搜索完毕,其所在的强联通分量已经被处理,所以不用被其进行操作。

对于一个强连通分量图,容易得到,在该图中只有一个节点 u 满足 lowu=dfnu,且该点一定是在 DFS 过程中被访问的第一个节点。因为它的 dfnlow 值最小,不会被该强连通分量中的其他节点所影响。

因此,在回溯的过程中只需要判断 dfnu=lowu 是否成立,如果成立,那么节点 u 以及上方的节点构成一个强连通分量。

3. 代码实现#

Copy
stack<int> sta; vector<int> SCC[N];//记录强连通分量中的点。 int tim = 0, head[N], dfn[N], low[N], col[N], cnt = 0; //tim:时间戳。 //col_i:表示第i个点属于的强联通分量编号。 void tarjan (int u) { dfn[u] = low[u] = ++ tim;//初始该点的dfn=low=时间戳。 sta.push (u);//当前节点进栈。 insta[u] = true; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (!dfn[v]) { tarjan (v); low[u] = min (low[u], low[v]); //没有被搜索过,用 low_v 值更新 low_u 值。 } else if (insta[v]) { low[u] = min (low[u], dfn[v]); //搜索过的节点在栈中,用 dfn_v 值更新 low_u 值。 } } if (dfn[u] == low[u]) {//找到一个 SCC。 cnt ++;//编号+1。 int v; while (u != v) { v = sta.top ();//不断取栈顶。 sta.pop ();//出栈。 insta[v] = false;//不在栈中。 col[v] = cnt;//v点属于第cnt个SCC中。 SCC[cnt].push_back (v);//将v点加入第cnt个SCC中。 } } }

易证,时间复杂度为 O(n+m)

四.例题讲解#

P2341 [USACO03FALL][HAOI2006]受欢迎的牛 G#

将爱慕关系建为一个有向图,那么在同一个强连通分量中的牛都互相爱慕。

那我们可以将强连通分量看做一个点,缩点后的奶牛就不会出现互相爱慕的情况了。

由题面可知,只有不爱慕其它奶牛才能当明星,那么我们就要在缩点后的图找出度为 0 的点。

然后得到两个结论:

1. 缩点后,如果只有一个点出度为 0,则明星的数量为这个点的强连通分量的大小。

2. 缩点后,如果有多个点出度为 0,那么没有明星。

这样就解决了。

P2272 [ZJOI2007]最大半连通子图#

tarjan 算法缩点后去重边,题目就变成了求最长链以及最长链个数。

缩点后的图是一个 DAG,拓扑排序跑 DP 转移即可。

posted @   cyhyyds  阅读(494)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
CONTENTS