割点和割边
割点与割边是在无向图中讨论的问题。
割点,即删除某个顶点,原图不再连通,这样的顶点称为割点。有时候也叫 “割顶”。割边是与割点性质类似的边(有时候叫做 “桥”)。求割点和割边同样利用Tarjan算法的 dfn[ ]数组和 low[ ]数组。
注意由于不同于求强连通分量,割点和割边是无向图上的问题,因此 low[ ] 数组的含义有细微变化,见后文。
Tarjan算法求割点
在Tarjan算法求强连通分量的过程中,讨论了 dfn[i] = low[i] 的情况,这意味着结点 i 不能通过其子孙结点回到更早的时间戳了,也就是说顶点 i 是一个强连通分量的起点。
同样也可以用 dfn[i] 和 low[i] 的关系,确定顶点 i 是否为割点。
分以下两种情况:
- 顶点 u 是搜索树的根节点。
此时,如果在搜索树中,顶点 u 有两个以上的孩子。则顶点 u 一定是割点,因为此时如果去掉顶点 u ,那么其不同子树之间的结点必定不可到达。 - 顶点 u 不是搜索树的根节点。
此时,如果对于边 (u,v)如果 low[v] >= dfn[u],则说明结点u是一个割点。
low[v] >= dfn[u],意味着结点v不能通过除边(u,v)以外的边回到其搜索树中的祖先结点,那么当去掉结点u时,结点v便会和其祖先结点分开。此时结点u就是一个割点。
代码
void Tarjan(int u, int fa)
{
dfn[u] = ++dfs_clock;
low[u] = dfn[u];
int child = 0;
for(int i=head[u];i;i=edge[i].next)
{
int v = edge[i].to;
if(!dfn[v])
{
child++;
Tarjan(v, u);
low[u] = min(low[u], low[v]);
if(low[v]>=dfn[u])
{
iscut[u] = 1;
}
}
else if(dfn[v]<dfn[u]&&v!=fa)
low[u] = min(low[u], dfn[v]);
}
if(fa==-1&&child==1)
iscut[u] = 0;
}
关于到底是 low[u] = min(low[u],dfn[v]) 还是 low[u] = min(low[u],low[v]) 的问题,求强连通分量时,两种写法都可以,但是求割点时只有 low[u] = min(low[u],dfn[v]) 是正确的,具体原因我目前也没明白。所以最好统一一下,都写成 low[u] = min(low[u],dfn[v])
Tarjan算法求割边
基本思路与求割点一致,当一条边(u,v),有 low[v] > dfn[u],则如果不经过边(u,v),顶点v不能访问到更早的时间戳,边(u,v)是一条割边。
int Tarjan(int u, int fa)
{
dfn[u] = ++deep;
int lowu = dfn[u], child = 0;
int sz = G[u].size();
for(int i=0;i<sz;i++)
{
int v = G[u][i];
if(dfn[v]==0)
{
int lowv = Tarjan(v, u);
lowu = MIN(lowu, lowv);
if(lowv>dfn[u])
{
int from = u, to = v;
if(from>to) //做了一个从小序号开始大序号结束的处理
{
int t = from;
from = to;
to = t;
}
bridge[++pb].from = from;
bridge[pb].to = to;
}
}
else
{
if(v!=fa&&dfn[v]<dfn[u])
lowu = MIN(lowu, dfn[v]);
}
}
low[u] = lowu;
return lowu;
}
双连通分量
针对无向图,有一个类似有向图强连通分量的定义叫做双连通分量,分为 “点双连通分量” 和 “边双连通分量”。
点双连通分量是原图的一个极大子图,其中任意两点都至少存在两条点不重复路径,也就是说这是一个没有割点的极大子图。特殊的,离散的顶点,和两点一边的联通分量也属于点双连通分量,因为这些联通分量也没有割点。
点双连通分量的性质:
- 点双连通分量内部的任意两条边都在同一个简单环中。
- 不同的点双连通分量,有且只有一个公共点,且这个点一定是原图的割点。
- 任意割点都是至少两个不同点双连通分量的公共点。
边双连通分量是一个没有割边(桥)的极大联通子图。离散的点是边双连通分量,但两点一边组成的联通分量不是,因为其具有割边。
边双连通分量具有下面的性质:
4. 从原图中去掉所有割边,剩下的联通分量为双连通分量。
5. 不同的边双连通分量之间没有公共点和边。
由于双连通分量是利用割点和割边定义的,所以求双连通分量的过程就是求割点和割边的过程,关键在如何保存信息,这一点需要按照具体题目来分析。
例题
求割点模板:
洛谷P3388 【模板】割点(割顶).
UVA315 Network.
求割边模板:洛谷P1656 炸铁路.
求去掉一个点,能得到多少连通块: 2020ICPC·小米 网络选拔赛第一场:D - Router Mesh.
并查集+点双连通分量:HDU3749 Financial Crisis.
思维+点双连通分量:洛谷P3225 [HNOI2012]矿场搭建.
边双连通分量模板:洛谷T103489 【模板】边双连通分量.
图论综合题,涉及双联通分量,思维难度较大:
ACM-ICPC CERC 2015 Juice Junctions.