割点与割边
创建日期:2025-02-07
割点和割边的定义
割点:
对于无向图 \(G\),若存在节点 \(v\) 使删除 \(v\) 及与 \(v\) 相连的边后,连通块数量增加,则称 \(v\) 是 \(G\) 的割点(又称割顶)。
注意:只有点数 \(\ge 3\) 的无向图才有可能有割点。
割边:
对于无向图 \(G\),若存在边 \(e\) 使删除 \(e\)后,连通块数量增加,则称 \(e\) 是 \(G\) 的割边(又称桥)。
注意:只有点数 \(\ge 2\) 的无向图才有可能有割边。
割点和割边的判定方法
割点:
我们使用Tarjan来判定割点。
设有点 \(x\) 及 \(x\) 的子节点 \(y\).
若 \(low_y \ge dfn_x\),则删除 \(x\) 后,从 \(y\) 出发,一定到达不了 \(x\)、\(x\) 的祖先、\(y\) 的兄弟节点,\(y\) 和 \(y\) 的后代从图中分割出,故 \(x\) 可能是原图的割点。
易知,若 \(x\) 的度数为1,即使删除 \(x\),连通块数量也不会发生改变。故当 \(x\) 不是叶子节点且 \(degree_x = 1\) 时,即使存在子节点 \(y\) 满足 \(low_y > dfn_x\),\(x\) 也不为割点。
什么节点可能同时满足非叶子且度数为1呢?只可能是搜索树的根!所以割点的判定条件应该为存在 \(x\) 的孩子 \(y\) 满足:
其中,\(root\) 为当前搜索树的根。
为了防止原图中存在重边,可以统计 \(x\) 的孩子数量 \(cnt\),并将条件改为:
由上可得出Tarjan函数:
void Tarjan(int u, int root) {
low[u] = dfn[u] = ++tot;
int cnt = 0;
for (int v : e[u]) {
if (!dfn[v]) {
Tarjan(v, root);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u] && (u != root || ++cnt >= 2))
cut[u] = true;
} else
low[u] = min(low[u], dfn[v]);
}
}
割边:
割边的判定方法与割点相似,判定条件仅为:
但由于无向图会建立双向边,\(y\) 一定可以找到 \(x\) 并通过 \(low_v = \min(low_v, dfn_u)\) 更新 \(low_v\),所以一定有 \(low_y \le dfn_x\)。为了解决这个问题,我们可以为每组双向条边存储一个编号,并记录每个点是从哪个编号的边来的,在遍历是跳过编号相同的边。
注意:因为图中可能存在重边,所以不能记录每个点从哪个点来的,一定要记录是从哪个编号的边来的。
模版代码如下:
struct edge {int v,id;};
void Tarjan(int u, int from) {
low[u] = dfn[u] = ++tot;
for (edge tmp : e[u]) {
int v = tmp.v, id = tmp.id;
if (id == from)
continue;
if (!dfn[v]) {
Tarjan(v, id);
low[u] = min(low[u], low[v]);
if (low[v] > dfn[u])
bridge[id] = true;
} else
low[u] = min(low[u], dfn[v]);
}
}

浙公网安备 33010602011771号