对于强连通分量的操作与运用作了解释
一.强连通分量的相关概念
强连通图
在一个有向图中,存在一条路径,使得所有的节点都被经过至少一次,那么这样的图称作强连通图。下图就是一个强连通图:
强联通分量
在强联通图的基础上加入一些点和路径,使得现在的图不再强联通,那么原来强联通的部分称作强连通分量。下图绿色部分就是强联通分量:
二.强联通分量的作用
在解决图论问题时,我们可以利用强联通分量的知识,将图中的各个强连通分量都缩成一个点,便于解决问题。有时,通过求强连通分量,可以得出图中环及环的长度。
三.\(\text{Tarjan}\)
\(1.\)
先考虑一下强连通分量的性质:存在一条路径可以从任意一点出发再到达该点。
在查找过程中,可以对经过的点标记。发现节点 \(u\) 连向的节点 \(v\)
\(2.\)
需要对每一个节点 \(u\) 创建以下两个变量:
\(dfn_u:\) 表示 \(DFS\) 时节点 \(u\)
\(low_u:\) 表示节点 \(u\) 能够回溯到的最早的在栈中的节点。用定义来解释:设以节点 \(u\) 为根的子树为 \(subtree_u\),那么 \(low_u = \min_{(dfn_v)}\) \((v \in subtree_u)\)。
然后我们就能轻易得出三个性质:
- \(1.\) 以 \(u\) 为根节点的子树中的任意一个节点的 \(dfn\) 值都小于 \(dfn_u\)。
- \(2.\) 从根出发的一条路径上的 \(dfn\)
- \(3.\) 从根出发的一条路径上的 \(low\)
接下来 \(DFS\)
搜索过程中,对于相邻的节点 \(u\) 和 \(v\),考虑以下三种情况:
- \(1.\) 节点 \(v\) 未被访问过:继续对节点 \(v\) 进行 \(DFS\)。回溯过程,用 \(low_v\) 更新 \(low_u\)。因为存在节点 \(u\) 到 \(v\) 的直接路径,那么节点 \(u\) 能回溯到栈中的节点,节点 \(v\)
- \(2.\) 节点 \(v\) 被访问过,已经在栈中:用 \(dfn_v\) 来更新 \(low_u\)。
- \(3.\) 节点 \(v\) 被访问过,但不在栈中:说明 \(v\)
对于一个强连通分量图,容易得到,在该图中只有一个节点 \(u\) 满足 \(low_u=dfn_u\),且该点一定是在 \(DFS\) 过程中被访问的第一个节点。因为它的 \(dfn\) 和 \(low\)
因此,在回溯的过程中只需要判断 \(dfn_u=low_u\) 是否成立,如果成立,那么节点 \(u\)
\(3.\)
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\)