图论——割点与割边
GJX_Algorithm · · 算法·理论
割点与割边
割点
前置知识
-
建议先学强连通分量,学会
dfn 数组与low 数组后再来学习割点。 -
点双连通:在无向图中,删除一个点(不是
x 或者y )后,点x 和点y 仍然能够彼此到达,那么称x 和y 是点双连通的。 -
边双连通:在无向图中,删除一条边后,点
x 和点y 仍然能够彼此到达,那么称x 和y 是边双连通的。 -
点双连通不具有传递性(如图),边双连通具有传递性。
割点的定义,结论,与判定
-
定义:在无向图
G 中,若删除cur 后,连通块的数量增加,则cur 称为无向图G 的一个割点(割顶)。 -
结论:至少有 3 个点的无向图,才可能存在割点。
-
判定:
-
若搜索树中,有从
cur 到nxt 的连边,当low_{nxt} \ge dfn_{cur} 时,说明nxt 能到达的最小时间戳在cur 的时间戳之上,nxt 被cur 与cur 之前的结点“隔开”,cur 可能是割点。 -
对于所有可能的割点,只要
cur 不是搜索树的根结点,或者cur 是根结点,但是cur 的子结点大于 1 个,那么cur 就是割点。
-
核心代码
void tarjan(int cur)
{
int cnt = 0; //用来统计cur有几个子结点
low[cur] = dfn[cur] = ++tot; //打上时间戳
for (int nxt : nbr[cur])
{
if (dfn[nxt] == 0) //nxt还没去过
{
tarjan(nxt); //递归继续搜索
low[cur] = min(low[cur], low[nxt]); //维护cur能到达的最小时间戳low[cur]
if (low[nxt] >= dfn[cur])
{
cnt++; //nxt还没去过,说明nxt是cur在当前搜索树中的一个子结点
if (cur != root || cnt >= 2) //排除根结点且只有一个子结点的情况
ans[cur] = 1;
}
}
else low[cur] = min(low[cur], dfn[nxt]); //nxt已经去过了,所以nxt还没有回溯,不能用low[nxt]
}
return ;
}
例题与练习
-
P3388(模板)
-
UVA10199(模板)
-
CF1761E
割边
割边的定义、性质与判定
-
割边的定义:在无向图中,若删除一条边
e 后,连通块的数量增加,那么称边e 为这张无向图的一条割边。 -
割边的性质:
-
割边一定不在环上,在环上的一定不是割边。
-
割边涉及的通常是“必经边”问题。
-
若删除一条割边,则联通块数量加一。
-
-
割边的判定:
-
同样先维护出
dfn 值与low 值。 -
如图,若
low_{nxt} > dfn_{cur} ,则边cur \to nxt 为割边。
-
核心代码
void edge_add(int u, int v)
{
a[++cnt1] = {u, v};
return ;
}
void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++tot;
for (int i = head[u]; ~i; i = nxt[i])
{
int v = to[i];
if (dfn[v] == 0)
{
tarjan(v, u);
low[u] = min(low[u], low[v]);
if (low[v] > dfn[u]) edge_add(u, v);
}
else if (v != fa) low[u] = min(low[u], dfn[v]);
}
return ;
}
例题与练习
-
T103481
-
P1656
-
P7687