图的连通性问题
前言:2023.2.2-2.3第一次接触,被打得落花流水,如今值2023.5.14——入坑 \(OI\) 9个月祭&&距离退役 \(1\) 年祭,回首连通性问题,柳暗花明,豁然开朗。时间果然能抚平一切。
主要思想:Tarjan算法。
Tarjan算法:
主要为记录两个数组 \(dfn_i\) 和 \(low_i\)。
\(dfn_i\) 表示将图进行 dfs 遍历的时间戳(遍历到的顺序)。
\(low_i\) 表示 \(min(\)这个点为根的 dfs 生成树的子树中节点的 \(low_i\) 的最小值,非其子树点中能仅通过一条非树边到达他的点的 \(dfn_i\) 的最小值 \()\)。
于是对于与 \(u\) 连边的节点 \(v\):
若 \(v\) 是 \(u\) 子树上的点,则 \(low_u=min(low_u,low_v)\)。
否则若不是,则 \(low_u=min(low_u,dfn_v)\)。
Tarjan算法的主要流程便是通过 dfs 的方式在标记每个遍历到的点的 \(dfn_i\) 的同时,对 \(low_i\) 进行赋值,同时根据不同的题目要求求解不同的连通分量/割点/割边等等。
通常判断要点为:
\(v\) 是 \(u\) 在搜索树上的儿子的情况下,若 $low_v>dfn_u $ ,则……
边的双连通问题
\(v\) 是 \(u\) 在搜索树上的儿子的情况下,若 $low_v>dfn_u $,则判断这条边为割边
简略证明:若 \(u\) 的儿子 \(v\) 不能通过一条非树边回到 \(u\) 以上的节点,则意味着 \(u\) 到 \(v\) 仅有一条路径,即这条边为割边。
根据以上结论,则可找到其双连通分量。
核心代码:
inline void tar(ll x,ll fa) {
dfn[x]=low[x]=++num;
for (ll i=head[x];i;i=nextx[i]) {
ll y=ver[i];
if (!dfn[y]) {
tar(y,i);
low[x]=min(low[x],low[y]);
if (low[y]>dfn[x]) bri[i]=bri[i^1]=1;
}
else if (i!=(fa^1)) { //若这条边不是上一条边的反向边
low[x]=min(low[x],dfn[y]);
}
}
}
inline void dfs(ll x) {
c[x]=1;
ans[dcc].push_back(x);
for (ll i=head[x];i;i=nextx[i]) {
ll y=ver[i];
if (c[y]||bri[i]) continue;
dfs(y);
}
}
这里注意,求点双的时候尽量不要在 tarjan 里直接求,原因如此。
模板题:边双连通分量
点双联通问题
1.\(v\) 是 \(u\) 在搜索树上的儿子的情况下,若 $low_v \ge dfn_u $,则判断这条边为割点
简略证明:若 \(u\) 的儿子 \(v\) 通过一条非树边最高仅能到达 \(u\),则意味着 $v $ 想要到达 \(u\) 以上必须要通过 \(u\),即 \(u\) 是割点。
2.若 \(u\) 为根点且有超过两棵子树的情况下,\(u\) 一定为割点。
简略证明:若满足上述条件,把 \(u\) 去掉后他的子树一定会分离。
根据以上结论,则可找到其点双连通分量。
核心代码:
inline void tar(ll x,ll fa) {
dfn[x]=low[x]=++num;
s.push(x);
if (head[x]==0&&fa==0) ans[++dcc].push_back(x);
for (ll i=head[x];i;i=nextx[i]) {
ll y=ver[i];
if (!dfn[y]) {
tar(y,x);
low[x]=min(low[x],low[y]);
if (low[y]>=dfn[x]) {
++dcc;
ll T;
while (T!=y) {
T=s.top();
s.pop();
ans[dcc].push_back(T);
color[T]=dcc;
}
ans[dcc].push_back(x);
}
}
else low[x]=min(low[x],dfn[y]);
}
}
强连通性问题
若 \(dfn_u=low_u\),则这个点为当前强连通分量的根,意味着直到下一个 \(dfn_u'=low_u'\),这之间的所有点都为以 \(u\) 为根的强连通分量
简略证明:若当前点的 \(dfn_u \ne low_u\),则它一定可以到达除了它连边以外的其他点,则他一定在某个连通分量里。
核心代码:
inline void solve(ll u) {
q.pop();
each[tot]++,vis[u]=0;
}
inline void tarjan(ll u) {
vis[u]=dfn[u]=low[u]=++cnt;
q.push(u);
for (ll i=0;i<G[u].size();++i) {
ll v=G[u][i];
if (!dfn[v]) {
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if (vis[v]) low[u]=min(low[u],dfn[v]);
}
if (dfn[u]==low[u]) {
++tot;
while (q.top()!=u) solve(q.top());
solve(u);
}
}
有用的结论:强连通图每个点一定都有出度和入度,但每个点都有出度和入度的有向无环图不一定是强连通图。
圆方树
构造:将一个无向连通图的所有点双拆开后,增加一个方点将其与这个点双内所有的点连边。
一个结论:每一对相邻且连通的点双之间一定有公共点,这个点即是两个点双之间的割点。
证明:两个相邻且联通的点双一定会有割点将其分离,而这个割点一定与两个点双共点。
由上面的结论可推出(广义)圆方树的性质之一:
圆方树上的圆点和方点一定交替出现。
同时一个点是割点的充要条件是:这个点在圆方树上的度数一定大于 \(1\)。

浙公网安备 33010602011771号