【2024-ZR-C Day 4】图论(1):强联通分量、Kosaraju 算法、边双连通分量、DFS 树、Boruvka 算法

1. 强连通分量

1.1. 定义

有向图中,选取一个点集 \(S\),若对于 \(S\) 中的任意两点 \(u, v\),都满足 \(u\) 可以到达 \(v\),则称 \(S\)强连通的

强连通分量是图中一个极大的强连通的点集。

性质:把一个有向图通过强连通分量缩点后,新的图是一个 DAG.

1.2. Kosaraju 算法

无向图中,求解连通分量只需要按照 \(1\)\(n\) 的顺序依次考虑每个点。
考虑到 \(i\) 点时,如果 \(i\) 点未被访问过,就说明找到了一个新的连通分量,从 \(i\) 点开始 DFS 即可找到i所在的连通分量。

有向图中,上述算法不成立。

假设 \(A\)\(B\) 是两个不同的强连通分量,且 \(A\) 可以到达 \(B\),那么只有先访问 \(B\),再访问 \(A\),才能使得上述算法成立。
也就是说,遍历顺序要保证 \(B\) 中至少有一个点排在所有 \(A\) 中的点之前。
考虑缩点后得到的 DAG,每次应访问出度为 \(0\) 的点,即按照拓扑序的逆序访问缩点后的所有节点。

Kosaraju 算法的思想:

  1. 把图中所有边反向,再按照 \(1\)\(n\) 的顺序去DFS,到达每个点时将其入栈,得到每个点的出栈序列 \(q_1, q_2, \ldots, q_n\),即为缩点后的拓扑序.
  2. 按照 \(q_n,\ldots, q_1\) 的顺序去 DFS 原图就是一个合法的顺序。

因为把边反向与否不改变强连通分量,所以得到一个简化的算法:

  1. 按照 \(1\)\(n\) 的顺序去 DFS 原图,得到每个点的出栈序列 \(q\).
  2. 按照 \(q_n\ldots q_1\) 的顺序去 DFS 反图,依次得到每个强连通分量。
#include <bits/stdc++.h>
using namespace std;

bool vis[N];
vector<int> g[2][N];
stack<int> stk;

void dfs1(int u)
{
	vis[u] = 1;
	for(auto v : g[0][u])
		if(!vis[v]) dfs1(v);
	q.push(x);
}

void dfs2(int u, int id)
{
	vis[u] = 0, bel[u] = id;
	for(auto v : g[1][u])
		if(vis[v]) dfs2(v, root);
}

int main()
{
	ios :: sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	for(int i = 1; i <= n; i++)
		if(!vis[i]) dfs1(i);
	while(!stk.empty())
	{
		int u = stk.top(); stk.pop();
		if(vis[u]) dfs2(u, ++idx);
	}
	return 0;
}

但这样的时间复杂度还是不够优秀,怎么办?

回顾此算法的执行过程,需要访问 \(O(n)\) 个点,遍历所有 \(m\) 条边只是为了判断一个点是否遍历过。
那么考虑直接维护每个点是否遍历过。
\(S_x\) 表示 \(x\) 的出边对应的顶点集合,\(T\) 表示未遍历过的点的集合,只有 \(S_x\cap T\) 中的点是有用的。
可以将 \(S_x\)\(T\) 用 bitset 存储,也就是 \(\frac{n}{w}\) 个 unsigned int.
只需要遍历 \(\frac{n}{w}\)\(S_x\)\(T\) 的值 \(t\),然后遍历 \(t\) 二进制中为 \(1\) 的位即可。
时间复杂度 \(O\left(\frac{n^2}{32}\right)\).


2. DFS 树

一张图的 DFS 树是在对其进行 DFS 时,所经过的所有有效边形成的树结构。

在构造题中,通常我们用到的是无向图的 DFS 树。

无向图的 DFS 树满足所有非树边都是返祖边


3. 边双连通分量

3.1. 定义&性质

一个无向连通图边双连通,当且仅当对于任意两个点,存在两条不相交的路径。

边双连通分量是图中一个极大的边双连通的点集。

定义割边为:在无向图中,该边所在连通块不再连通的边。
边双连通的定义中,“存在两条不相交的路径”显然等价于“如果无论删去哪条边都不能使它们不连通”,故一个无向连通图边双连通的充要条件是没有割边

一个无向图边双缩点后是一棵树,所有树边是割边。
(若缩点后不是一颗树,说明还存在环,则环上所有的点在一个边双内,这与边双连通分量“极大”的定义矛盾。)

3.2. 算法解析

由【3.1. 定义&性质】,求出所有边双连通分量只需求出所有割边,由割边断开后得到的一个连通块就是一个边双连通分量。

取原图的 DFS 树,只有树边有可能是割边。

一条非树边(由【2. DFS 树】的性质知,只可能是返祖边)会覆盖掉一条链上的树边,这些树边就会被 ban 掉,使用树上差分(或树剖)维护,剩下的边就是割边。


4. Boruvka 算法

一个神秘的 MST 求法。

维护一些连通块(初始都是单点),并进行若干轮连边。

每一轮中,找到每个连通块连向其它连通块的最小边(需要做不等离散化,以边权为第一关键字,以边的编号为第二关键字)并连上,这一条边一定会在 MST 中。
(可以根据 Kruscal 的流程证明。)

每轮连通块数量至少减半,最多进行 \(O(\log n)\) 轮连边。
最劣时间复杂度 \(O((n + m) \log n)\).

Boruvka 算法适合用来求完全图(或非常稠密图)的 MST.


5. 例题

5.1. CF555E Case of Computer Network

给定一张 \(n\) 个点 \(m\) 条边的无向图和 \(q\) 组有向点对 \((s, t)\)
询问是否存在使得所有 \(s\) 都能到达 \(t\) 的无向图中每条边的定向方案。
\(n,m,q \le 2 \times 10^5\).

有一个显然的结论:

在边双连通分量中可定向,使得内部构成强连通。

只需取边双中的 DFS 树,使树边向下,非树边向上即可。

则在本题中,若 \(s, t\) 在同一边双,则不必考虑(一定可以)。
那么只需考虑 \(s, t\) 在不同边双的情况。

考虑将原图按边双缩点成一颗树。
对于每个询问 \(s, t\),在树上的路径一定为 \(s\) 到根之间向上、根到 \(t\) 之间向下。

可以使用树链剖分+线段树区间覆盖来做,做的过程中 check 当前要定向的边中是否有边已经被定了相反的方向。

5.2. CF1364D Ehab's Last Corollary

给定一个无向连通图和正整数 \(k\),求一个大小为 \(\lceil \frac{k}{2} \rceil\) 的独立集或大小不超过 \(k\) 的环(求出任意一个即可)。

首先找到图中的最小环。

  • 若环的大小不超过 \(k\),则已经得到了解。
  • 若环的大小大于 \(k\):对环进行黑白染色,由狄利克雷抽屉原理,必然存在一个颜色全部相同的大小大于 \(\lceil \frac{k}{2} \rceil\) 的独立集,从其中任选 \(k\) 个点即可。

问题转化为如何求最小环。

对于一条非树边 \((u, v)\),定义 \(w_{u, v} = |dep_v - dep_u|\).
找到图中 \(w\) 最小的非树边,它和 \(u, v\) 之间的所有树边构成最小环。

5.3. CF1103C Johnny Solving

给定一张 \(n\) 个点 \(m\) 条边的无向连通图,以及一个整数 \(k\),保证每个点度数 \(\ge 3\),你需要找到一个至少有 \(\lceil \frac{n}{k} \rceil\) 个点的路径,或者找出 \(k\) 个环,其中每个环的环长都不是 \(3\) 的倍数,且每个环中至少有一个点在这 \(k\) 个环里只出现一次。

任取一棵 DFS 树。

  • 如果最大深度 \(\lceil \frac{n}{k} \rceil\),则找到了路径。
  • 否则,根据树的性质,至少有 \(k\) 个叶子。
    由题中“保证每个点度数 \(\ge 3\)”的限制知,对于每个叶子,其至少有两条返祖边。
    任取其中两条,根据模 \(3\) 的余数分讨后不难发现,一定存在一个包含这个叶子的环,满足其环长不是 \(3\) 的倍数。

5.4. LG5811 [IOI2019] 景点划分

给定一个 \(n\) 个点、\(m\) 条边的无向连通图,以及三个整数 \(a, b, c\),满足 \(a + b + c = n\),你需要将 \(n\) 个点分成三个集合 \(A, B, C\),大小分别为 \(a, b, c\),使得其中至少两个集合是连通的 (集合中的任意两个点能只经过该集合内的点互相到达),或者报告无解。

不妨设 \(a \le b \le c\),则让集合 \(A, B\) 连通的限制一定是最松的。

考虑原图是树的情况。我们想找到一条边,让它两侧的点数分布尽量均匀。
考虑重心,找到所有以重心 \(u\) 为根的子树中最大的一个,连接这棵子树与重心的边即为该边。有解当且仅当子树 \(size \ge a\).
(为什么是重心?因为根据重心的性质,\(\forall v \in G_u: siz_{v} \le \lfloor \frac{n}{2} \rfloor\),而题目中其实保证了 \(a \le b \le \lfloor \frac{n}{2} \rfloor\).)

回到原题,任取一棵 DFS 树,找出重心 \(u\).
\(u\) 上方的子树为 \(T\)\(u\) 的其它子树为 \(S_1, S_2, \cdots , S_k\).
根据【2. DFS 树】中的性质,无向图的 DFS 树中只有树边和返祖边,即 \(T\) 可以和一些 \(S_i\) 有连边,而 \(S_1, \cdots, S_k\) 之间无连边。

有解的情况只有以下两种:

  1. 最大子树 \(\ge a\).
  2. \(T\) 与所有与其相连的 \(S_i\) 中的点数之和 \(\ge a\).

第【2】种情况可能不太明显。
若不满足【1】,则 \(|S_i|, |T| < a\).
枚举所有 \(S_i\),每次动态地与 \(T\) 合并,直到总点数 \(\ge a\) 停止。根据上面的性质,此时的点数一定小于 \(2a\),剩余的所有点构成一个连通块,由 \(a\le b\le c\) 知,这个块的大小不小于 \(b\).

posted @ 2024-07-20 14:27  心灵震荡  阅读(7)  评论(0编辑  收藏  举报