连通性问题
连通性问题
tarjan:连通性问题永远的神。
双连通与割点、割边
-
点双连通:在无向图中,删除一个点(不是 或者 )后,点 和点 仍然能够彼此到达,那么称 和 是点双连通的;
-
边双连通:在无向图中,删除一条边后,点 和点 仍然能够彼此到达,那么称 和 是边双连通的;
-
性质 : 点双连通不具有传递性,但边双连通具有传递性;
-
割点
-
在无向图 中,若删除 后,连通块的数量增加,则 称为无向图 的一个割点(割顶)。
-
结论:至少有 个点的无向图,才可能存在割点;
-
割点的判定
- 若搜索树中,有从 到 的连边,当 时,说明 能到达的最小时间戳在 的时间戳之上, 被 与 之前的结点“隔开”, 可能是割点。只要 不是搜索树的根结点,或者 是根结点,但是 的子结点大于 个,那么 就是割点。
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18void tarjan(int x)
{
dfn[x] = low[x] = ++num; //打时间戳
int cnt = 0; //统计子 结点个数
for (auto& [nxt, w] : nbr[x])
if (!dfn[nxt]) //没去过 $nxt$
{
tarjan(nxt); //递归查找
low[x] = min(low[x], low[nxt]); //维护 $x$ 能达到的最小时间戳 $low_X$
cnt++;
if (low[nxt] >= dfn[x]) //$x$ 可能是割点
if (x != root || cnt >= 2) //排除根 结点只有 $1$个子 结点
cut[x] = true;
}
else //$nxt$ 去过,所以 $nxt$ 还没有回溯,不能用 $low_nxt$
low[x] = min(low[x], dfn[nxt]);
return;
}
-
-
割边
-
在无向图中,若删除 条边 后,连通块的数量增加,那么称 为一条割边。
-
性质:割边一定不在环上,在环上的边一定不是割边。
-
割边涉及的通常是“必经边”的问题。
-
不用证明:割边删除后,恰好增加 个连通块。
-
割边的判定
-
维护 和 之后,对于 到 的连边,若 ,则
bridge[i] = bridge[i^1] = true
(对于链式前向星的方法)。 -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
typedef int int32;
using namespace std;
const int N = 1e6 + 5;
int n, m, dfn[N], low[N], num, ans;
bool cut[N];
vector<pair<int, int>>nbr[N];
void tarjan(int x, int edge)
{
dfn[x] = low[x] = ++num;
for (auto& [nxt, w] : nbr[x])//这里的 $w$ 不是权值,是边的编号
if (!dfn[nxt])
{
tarjan(nxt, w);
low[x] = min(low[x], low[nxt]);
if (low[nxt] > dfn[x])
cut[w] = true;
}
else if (w != edge)
low[x] = min(low[x], dfn[nxt]);
return;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int x, y, z = i;
cin >> x >> y;
nbr[x].push_back({ y,z });
nbr[y].push_back({ x,z });
}
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i, 0);
for (int i = 1; i <= m; i++)
ans += cut[i];
cout << ans;
return 0;
}
-
-
例题
洛谷P3388
题意:给出一个 个点, 条边的无向图,求图的割点。
CF22C
题意:构造 个点 条边的无向连通图,且无重边,点 必须是割点。需特判无解。
- 有解时, 一定有上界和下界,下届为 ;
- 已经连接了 个点,不能有重边,因此 不能再连边,视为 不存在;
- 为了保证容纳的边尽可能多,要尽量连完全图,且不能破坏 的割点特征;
- 留 个单点只跟 保持连边,剩下的 个点构造完全图即可;
- 上界 。
代码:
1 |
|
洛谷P3469
题意:对于每个结点 求出,把与结点 关联的所有边去掉以后(不去掉结点 本身),无向图有多少个有序点 ,满足 和 不连通。
- 一个点 ,如果是不是割点,那么贡献一定是 ;否则贡献是 。其中 为 结点 满足: 是 的子 结点, 之前没有访问过且 ; 为 。
1 |
|
洛谷P1656
题意:求割边,模板题。
1 |
|
洛谷P7687
- 初始时,所有点都能访问 A 和 B 类服务;
- 如果一条边,非割边,意味着在环上,删除后不改变连通性,不可能是关键边;
- 关键边应该是割边的一个子集;
- 对于 到 的连边,两侧不能出现没有 A 或者没有 B 类服务的点;
1 |
|
边双连通分量
- 边双连通:若无向图中点 和 在删除任意连边后,仍然连通,称 和 是边双连通的;
- 边双连通分量:若无向图 中,存在一个极大子图 , 中没有割边,那么 是 的一个边双连通分量记为 E-DCC;
例题
洛谷T103489
题意:给定一个 个点 条边的无向图,求边双连通分量(e-dcc)数量。
1 |
|
CF51F
- 图可能不连通,视为多个毛毛虫的拼接,每两个操作 次;
- 对于一张连通图, 缩边双连通分量变为一棵树;
- 对于一棵树,操作叶子结点不会比操作叶子的父结点更优,所以叶子结点保留;
- 保留的主链越长越好,取树的直径最优,设长度为 ,且子结点为 个,;
- 设一个边双的结点数为 ,一个边双需要操作 次;
点双连通分量
-
点双连通:无向图中,若点 和 ,在删除任一点 ()后, 和 仍然连通,那么称点 和 是点双连通的。
-
点双连通分量:无向图 中,若存在极大子图 , 中没有割点,那么 称之为 的一个点双连通分量,记为 V-DCC。
推论:
-
边双连通分量由割边连接,所以一个点只能在 个 E-DCC 里;
-
点双连通分量由割点链接,而 个点可以链接多条边,因此 个点可以在多个 V-DCC 里;
-
若 个点在超过 个 V-DCC 中,则该点一定是割点;
-
当研究一个 V-DCC 时,其内部没有“割点”,当研究整个图时,多个 V-DCC 的交点就是割点;
-
一条边只能在一个 V-DCC 中。
例题
洛谷P8435
题意:对于一个 个节点 条无向边的图,请输出其点双连通分量的个数,并且输出每个点双连通分量。
1 |
|