From b8590107ce73551338a0af0f1d4eb5a027198a8b Mon Sep 17 00:00:00 2001 From: CurryWOE <76390659+CurryWOE@users.noreply.github.com> Date: Mon, 30 Oct 2023 17:41:00 +0800 Subject: [PATCH] =?UTF-8?q?Update=20=E7=82=B9(=E8=BE=B9)=E5=8F=8C=E8=BF=9E?= =?UTF-8?q?=E9=80=9A=E5=88=86=E9=87=8F.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增强连通,点双连通 --- ...36\351\200\232\345\210\206\351\207\217.md" | 173 ++++++++++++++---- 1 file changed, 140 insertions(+), 33 deletions(-) diff --git "a/docs/Graph/\347\202\271(\350\276\271)\345\217\214\350\277\236\351\200\232\345\210\206\351\207\217.md" "b/docs/Graph/\347\202\271(\350\276\271)\345\217\214\350\277\236\351\200\232\345\210\206\351\207\217.md" index 94c01da8b..d1401dc2b 100644 --- "a/docs/Graph/\347\202\271(\350\276\271)\345\217\214\350\277\236\351\200\232\345\210\206\351\207\217.md" +++ "b/docs/Graph/\347\202\271(\350\276\271)\345\217\214\350\277\236\351\200\232\345\210\206\351\207\217.md" @@ -1,5 +1,63 @@ +# 连通性相关 +# 强连通 +有向图,任意两点连通 +## 强连通分量缩点 +```cpp +vector e[N], sta, ne[N]; +array ed[N]; +int dfn[N], low[N], f[N], vis[N],in[N],dfsOrder, sccnum; +void init(int n) +{ + dfsOrder = sccnum = 0; + for (int i = 1; i <= n; ++i) + e[i].clear(); + sta.clear(); + memset(vis, 0, sizeof(int) * (n + 1)); + memset(dfn,0,sizeof(int)*(n+1)); + memset(in,0,sizeof(int)*(n+1)); +} +void tarjan(int u) +{ + dfn[u] = low[u] = ++dfsOrder; + sta.push_back(u); + vis[u] = 1; + for (auto &v : e[u]) + { + 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]) + return; + ++sccnum; + vector::iterator it=(--sta.end()); + for(f[u]=sccnum,vis[u]=0;*it!=u;--it) + f[*it]=sccnum,vis[*it]=0; + sta.erase(it,sta.end()); +} +void makeNewMap(int n,int m) +{ + for(int i=1;i<=n;++i) + if(!in[i]) + tarjan(i); + for(int i=1;i<=n;++i) + if(!dfn[i]) + tarjan(i); + for (int i = 0;i=dfn[u]) cut[u]=1; } - else if(v!=fa) + else if(v!=fa)//有向图改为else low[u]=min(low[u],dfn[v]); } if(fa==u && child>=2) @@ -38,15 +96,14 @@ void findCutVertex(int n) } } ``` -# 桥 +# 割边 删边,其他类似割点 修改一处:$low_v>dfn_u$,且不需要特殊处理根节点 -u-bridge[u] 是一条桥 +u-bridge[u] 是一条割边 ## 一些性质 -桥的两边一般是割点,除非只有一个点 - +割边的两边一般是割点,除非只有一个点 ```cpp vector e[N]; int dfn[N],low[N],dfsOrder,bridge[N]; @@ -62,7 +119,7 @@ void tarjan(int u,int fa) if(low[v]>dfn[u]) bridge[v]=u; } - else if(v!=fa) + else if(v!=fa)//有向图改为else low[u]=min(low[u],dfn[v]); } } @@ -75,24 +132,27 @@ void findBridge(int n) } } ``` -# 边双缩点 -把每个极大边双连通分量缩成一个点,原图变成一颗树,树上问题就比较好解决 - -tarjan访问每个点时把点进栈,当一个点 u 的后续点都访问完时,如果 u 是一个极大边双连通分量的,第一个访问的点,把栈内的点都拿出来,直到 u。同时给原来的点做一个映射。同一个映射的点,在同一个极大边双连通分量。 +# 边双连通 +没有割边的连通图是边双连通,点的边双连通具有传递性 +## 边双连通分量缩点 +tarjan访问每个点时把点进栈,当一个点 u 的后续点都访问完时,如果 u 是一个极大边双连通分量的,第一个访问的点,把栈内的点都拿出来,直到 u。同时给原来的点做一个映射。同一个映射的点,在同一个边双 新图可能需要旧图的信息,所以旧图的边信息存两份 ```cpp -vector e[N]; -struct edge +vector e[N],sta,ne[N]; +array ed[N]; +int dfn[N],low[N],f[N],dfsOrder,bccnum; +void init(int n) { - int u,v,w; -}ed[N]; -int dfn[N],low[N],dfsOrder,f[N],bccnum; -vector bcc; + dfsOrder=bccnum=0; + for(int i=1;i<=n;++i) + e[i].clear(); + sta.clear(); +} void tarjan(int u,int fa) { low[u]=dfn[u]=++dfsOrder; - bcc.push_back(u); + sta.push_back(u); for(int &v:e[u]) { if(!dfn[v]) @@ -100,7 +160,7 @@ void tarjan(int u,int fa) tarjan(v,u); low[u]=min(low[u],low[v]); } - else if(v!=fa) + else if(v!=fa)//有向图改为else low[u]=min(low[u],dfn[v]); } if(low[u]!=dfn[u]) @@ -114,28 +174,75 @@ void tarjan(int u,int fa) f[u]=bccnum; bcc.pop_back(); } -vector> d[N]; void makeNewMap(int m) { tarjan(1,1); for(int i=0;i e[N], sta; +int dfn[N], low[N], dfsOrder; +vector> ans; +void tarjan(int u, int fa) +{ + low[u] = dfn[u] = ++dfsOrder; + sta.push_back(u); + for (auto &v : e[u]) + if (!dfn[v]) { - d[u].push_back({v,ed[i].w}); - d[v].push_back({u,ed[i].w}); + tarjan(v, u); + low[u] = min(low[u], low[v]); + if (low[v] >= dfn[u]) + { + vector tmp; + while (sta.back() != v) + { + tmp.push_back(sta.back()); + sta.pop_back(); + } + sta.pop_back(); + tmp.push_back(v); + tmp.push_back(u); + ans.push_back(tmp); + } } - } + else if (v != fa) // 有向图改为else,重边需判断是否同一条无向边 + low[u] = min(low[u], dfn[v]); } -int main() +void makeNewMap(int n) { - for(int i=0;i tmp; + tmp.push_back(i); + ans.push_back(tmp); + } + } } ``` +# 圆方树 +原来的每个点对应一个圆点,每一个点双对应一个方点 + +对于每一个点双,它对应的方点向这个点双中的每个点连边 + +如果原图连通,则“圆方树”才是一棵树