双连通分量
定义
对于无向图中两点\(u,v\),若无论删去哪条边都不能使其不连通,则称\(u,v\)边双连通
对于无向图中两点\(u,v\),若无论删去哪个点都不能使其不连通,则称\(u,v\)点双连通
边双连通
我们发现使两点不连通的边其实就是桥(割边)
所以我们只需要把桥删了即可
void tarjan(int now,int fa) {
dfn[now]=low[now]=++cnt;
for (int i=beg[now];i;i=e[i].nex) {
int nex=e[i].to;
if (nex==fa) continue;
if (!dfn[nex]) {
tarjan(nex,now);
chkmin(low[now],low[nex]);
if (dfn[now]<low[nex]) e[i^1].cut=e[i].cut=1;
}
else chkmin(low[now],dfn[nex]);
}
}
void dfs(int now,int fa,int C) {
col[C].push_back(now);
bel[now]=C;
for (int i=beg[now];i;i=e[i].nex) {
int nex=e[i].to;
if (nex==fa || bel[nex] || e[i].cut) continue;
dfs(nex,now,C);
}
}
点双连通
我们发现,两个点双一定有一个割点连接,且这个割点被两个点双包含,所以我们可以直接根据强连通分量的方式,把点入栈,然后找到割点就不停弹出点,并全部存入点双
void tarjan(int now,int Fa) {
dfn[now]=low[now]=++cnt;st[++top]=now;
if (now==Fa && !beg[now]) col[++tot].push_back(now);
for (int i=beg[now];i;i=e[i].nex) {
int nex=e[i].to;
if (nex==Fa) continue;
if (!dfn[nex]) {
tarjan(nex,now);
chkmin(low[now],low[nex]);
if (dfn[now]<=low[now]) {
++tot;
while(1) {
col[++tot].push_back(st[top]);
if (st[top]==nex) {top--;break;}
top--;
}
col[tot].push_back(now);
}
}
else chkmin(low[now],dfn[nex]);
}
}
圆方树
圆方树主要用于处理仙人掌图(每条边在不超过一个简单环中的无向图)的问题
在刚刚的点双基础上,我们来构建一棵圆方树
我们将原来的点看做一个圆点,对于一个点双建一个方点,方点向该点双里的每个圆点连边
那么,每个点双就是一个菊花,然后整张图是由割点连接很多菊花
当原图是一个联通图时,我们构建的图就是一棵树
然后,我们考虑如何实现这个构建过程
由于圆方树基于点双,所以在求点双的过程中可以建出这棵树
void tarjan(int now,int fa) {
dfn[now]=low[now]=fa;
if (now==fa && !beg[now]) {
cntcol++;
E[cntcol].push_back(now);
E[now].push_back(cntcol);
}
st[++top]=now;
for (int i=beg[now];i;i=e[i].nex) {
int nex=e[i].to;
if (nex==fa) continue;
if (!dfn[nex]) {
tarjan(nex,now);
chkmin(low[now],low[nex]);
if (low[nex]<=dfn[now]) {
cntcol++;
while(1) {
E[cntcol].push_back(st[top]);
E[st[top]].push_back(cntcol);
if (st[top]==nex) {top--;break;}
top--;
}
E[cntcol].push_back(now);
E[now].push_back(cntcol);
}
else chkmin(low[now],dfn[nex]);
}
}
}
性质
对于一个点双中的两个点,他们之间简单路径的并集,恰好等于这个点双
考虑如何证明这个性质
首先,一条路径如果出了点双,一定不能再回来,否则违背了点双定义
基于此点,我们只需要证明一个点双中任意三个不同点\(x,y,z\),一定存在一条路径可以从\(x\),经过\(y\),到达\(z\)
考虑网络流
从源点向\(y\)建一条流量为\(2\)的边
从\(x,z\)向汇点连一条流量为\(1\)的边
对于图中的无向边就建两条流量为\(1\)的边
然后,因为每个点只能经过一次,所以我们要限制每个点只能流\(1\)
所以我们拆点,对于点\(i\)与\(i'\)连一条流量为\(1\)的边
那么如果最后的最大流为\(2\),就说明一定经过了\(y\)
根据最大流最小割定理,显然最小割时小于\(2\)的,所以我们要证明最小割大于\(1\)
这个证明等价于网络流中割掉任何一个点无法使图不连通
分类讨论一下
如果删掉\(x,z\)向汇点的边,根据点双定义,一定存在一条路径从\(y\)连向另一个每删边的点
如果删掉一个点拆点的边,因为点双是没有割点的图,所以剩下的图一定联通
如果删掉原图中的边,这跟点双定义,也是联通的
所以,最大流等于\(2\)
所以证毕