EVERYTHING HAPPENS FOR T|

wnsyou

园龄:2年4个月粉丝:19关注:16

2023-10-08 23:07阅读: 52评论: 0推荐: 0

连通性与 Tarjan

强连通分量和 Tarjan

OI-wiki Link

强连通分量(Strongly Connected Components,SCC),指的是极大的连通子图。

tarjan 算法,用于求图的强连通分量。

dfs 生成树

有向图的 dfs 生成树大致分为四种边:

  • 树边(tree edge):示意图中以黑色边表示,每次搜索找到一个还没有访问过的结点的时候就形成了一条树边。
  • 反祖边(back edge):示意图中以红色边表示(即 71),也被叫做回边,即指向祖先结点的边。
  • 横叉边(cross edge):示意图中以蓝色边表示(即 97),它主要是在搜索的时候遇到了一个已经访问过的结点,但是这个结点 并不是 当前结点的祖先。
  • 前向边(forward edge):示意图中以绿色边表示(即 36),它是在搜索的时候遇到子树中的结点的时候形成的。

如果结点 u 是某个强连通分量在搜索树中遇到的第一个结点,那么这个强连通分量的其余结点肯定是在搜索树中以 u 为根的子树中。结点 u 被称为这个强连通分量的根。

反证法:假设有个结点 v 在该强连通分量中但是不在以 u 为根的子树中,那么 uv 的路径中肯定有一条离开子树的边。但是这样的边只可能是横叉边或者反祖边,然而这两条边都要求指向的结点已经被访问过了,这就和 u 是第一个访问的结点矛盾了。得证。

Tarjan 求强连通分量

需要对每个节点 u 都维护以下几个信息:

  • dfnuu 在深度优先搜索时是第几个被访问的(人称豆腐脑序)
  • lowu:在 u 的子树中能够回溯到的最早的在栈中的结点。

很明显,对于每个 u 的后代 i 都有 dfnu<dfni。从根开始的一条路径上的 dfn 严格递增,low 严格非降。

用 dfs 来求 dfnlow,每搜索到一个新的节点就让它入栈,每找到一个强连通分类,就让它们出栈。

当节点 u 向节点 v 做遍历时,有三种情况:

  • v 未被访问,则对 v 进行 dfs,回溯时 low[u] = min(low[u], low[v]); 因为存在从 uv 的直接路径,所以 v 能够回溯到的已经在栈中的结点,u 也一定能够回溯到。
  • v 已被访问且仍在栈中,那么 low[u] = min(low[u], dfn[v]);
  • v 被访问过且已不在栈中,说明 v 已搜索完毕,其所在连通分量已被处理,所以不用对其做操作。

处理完以后,我们可以想到在一个强连通分量里有且仅有一个点 u 满足 dfnu=lowu,该结点一定是在深度遍历的过程中,该连通分量中第一个被访问过的结点,因为它的 dfnlow 值最小,不会被该连通分量中的其他结点所影响。

所以只要在回溯的过程中判断 dfnu 是否和 lowu 相等,若相等,则栈内 u 及它上方的节点构成一个 SCC。

const int N = 1e4 + 10;
int n, dfncnt, dfn[N], low[N], bel[N], stk[N], top, fstk[N], sc, sz[N], b[N], ans, sum, c[N];
vector<int> g[N];
void tarjan (int x) {
dfn[x] = low[x] = ++dfncnt, fstk[x] = 1, stk[++top] = x;
for (int i : g[x]) {
if (!dfn[i]) {
tarjan(i), low[x] = min(low[x], low[i]);
} else if (fstk[i]) {
low[x] = min(low[x], dfn[i]);
}
}
if (dfn[x] == low[x]) {
++sc;
while (1) {
sz[sc]++, fstk[stk[top]] = 0, bel[stk[top--]] = sc;
if (stk[top + 1] == x) {
break;
}
}
}
}

应用

我们可以将每个强连通分量都压缩成一个点(缩点),那么原图就会变成 DAG,可以进行拓扑排序等。

例题

P2746 [USACO5.3] 校园网Network of Schools | #10091. 「一本通 3.5 例 1」受欢迎的牛

割点和割边

OI-wiki Link

割点

对于一个无向图,如果把这个点删除后这个图的极大连通分量数增加了,那么这个点就是原图的割点(割顶)。

举个例子,这张图内的割点明显只有 2

首先我们先通过 DFS 给它们打上时间戳:

记录在 dfn 中,还需要另外一个数组 low,用它来存储不经过其父亲能到达的最小的时间戳。

好,和 Tarjan 扯上关系了,那么首先 Tarjan 预处理一下,可以得到定理:对于某个顶点 u,如果存在至少一个顶点 v(u 的儿子),使得 lowvdfnu,即不能回到祖先,那么 u 点为割点。

但是对于搜索树的根节点需要特殊处理:若该点不是割点,则其他路径亦能到达全部结点,因此从起始点只「向下搜了一次」,即在搜索树内仅有一个子结点。如果在搜索树内有两个及以上的儿子,那么他一定是割点了(设想上图从 2 开始搜索,搜索树内应有两个子结点:3456)。如果只有一个儿子,那么把它删掉,不会有任何的影响。比如下面这个图,此处形成了一个环。

#include <bits/stdc++.h>
using namespace std;
const int N = 2e4 + 10;
int n, m, dfncnt, dfn[N], low[N], bel[N], stk[N], top, fstk[N], sc, sz[N], cnt;
vector<int> g[N];
bool ans[N];
void tarjan (int x, int fa) {
int son = 0;
dfn[x] = low[x] = ++dfncnt, fstk[x] = 1, stk[++top] = x;
for (int i : g[x]) {
if (!dfn[i]) {
son++;
tarjan(i, x), low[x] = min(low[x], low[i]);
if (fa != x && low[i] >= dfn[x] && !ans[x]) {
ans[x] = 1, cnt++;
}
} else if (i != fa) {
low[x] = min(low[x], dfn[i]);
}
}
if (fa == x && son >= 2 && !ans[x]) {
ans[x] = 1, cnt++;
}
}
int main () {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m;
for (int i = 1, x, y; i <= m; i++) {
cin >> x >> y, g[x].push_back(y), g[y].push_back(x);
}
for (int i = 1; i <= n; i++) {
if (!dfn[i]) {
dfncnt = 0, tarjan(i, i);
}
}
cout << cnt << '\n';
for (int i = 1; i <= n; i++) {
if (ans[i]) {
cout << i << ' ';
}
}
return 0;
}

模板题 Link

割边

对于一个无向图,如果删掉一条边后图中的连通分量数增加了,则称这条边为桥或者割边。严谨来说,就是:假设有连通图 G={V,E}e 是其中一条边(即 eE),如果 Ge 是不连通的,则边 e 是图 G 的一条割边(桥)。

比如下图中红色边就是割边。

和割点差不多,不需要特殊考虑根节点,只要把 lowvdfnu 改为 lowv>dfnu 即可。

void tarjan (int x, int fa) {
dfn[x] = low[x] = ++dfncnt, fstk[x] = 1, stk[++top] = x;
for (int i : g[x]) {
if (!dfn[i]) {
tarjan(i, x), low[x] = min(low[x], low[i]);
if (low[i] > dfn[x] && !ans[x]) {
ans[x] = 1, cnt++;
}
} else if (i != fa) {
low[x] = min(low[x], dfn[i]);
}
}
}

本文作者:wnsyou の blog

本文链接:https://www.cnblogs.com/wnsyou-blog/p/tarjan.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   wnsyou  阅读(52)  评论(0编辑  收藏  举报
  1. 1 勝利への道 安藤浩和
  2. 2 Minecraft’s End Eric Fullerton
  3. 3 月光曲完整版 贝多芬 云熙音乐
  4. 4 平凡之路 (Live版) 朴树
  5. 5 Minecraft C418
  6. 6 Paradise NiziU
  7. 7 叫我,灰原哀 龙大人不喷火
  8. 8 心机之蛙,一直摸你肚子 ——《名侦探柯南》原创同人曲 炊饭,叶辞樱,温海,寒砧,南柯柯,小茜玛姬,盛姝,阿崔Ac,贝壳初,千湛,兮茶子DaYu,乔慕,黎鹿北,起千温卿,遮阳伞,曲悠
  9. 9 战 歌 此去经年
战 歌 - 此去经年
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 此去经年

作曲 : 此去经年

编曲 : 此去经年

啊啊啊啊啊

啊啊啊啊

啊啊啊

啊啊

加载中…

{{tag.name}}

{{tran.text}}{{tran.sub}}
无对应文字
有可能是
{{input}}
尚未录入,我来提交对应文字
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示