Skip to content

Commit

Permalink
Update 点(边)双连通分量.md
Browse files Browse the repository at this point in the history
新增强连通,点双连通
  • Loading branch information
CurryWOE authored Oct 30, 2023
1 parent 59c5d45 commit b859010
Showing 1 changed file with 140 additions and 33 deletions.
173 changes: 140 additions & 33 deletions docs/Graph/点(边)双连通分量.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,63 @@
# 连通性相关
# 强连通
有向图,任意两点连通
## 强连通分量缩点
```cpp
vector<int> e[N], sta, ne[N];
array<int,2> 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<int>::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<m;++i)
{
int u=f[ed[i][0]],v=f[ed[i][1]];
if(u==v)
continue;
ne[u].push_back(v);
}
}
```
# 割点
无向图,如果删除一个点后,这个图的极大连通分量数增加,那么该点为割点
如果删除一个点后,这个图的极大连通分量数增加,那么该点为割点
记录每个点的dfs序(dfn),和不通过其父亲能到达的最小dfs序(low)
Expand All @@ -23,7 +81,7 @@ void tarjan(int u,int fa)
if(u!=fa && low[v]>=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)
Expand All @@ -38,15 +96,14 @@ void findCutVertex(int n)
}
}
```
#
# 割边
删边,其他类似割点

修改一处:$low_v>dfn_u$,且不需要特殊处理根节点

u-bridge[u] 是一条桥
u-bridge[u] 是一条割边
## 一些性质
桥的两边一般是割点,除非只有一个点
割边的两边一般是割点,除非只有一个点
```cpp
vector<int> e[N];
int dfn[N],low[N],dfsOrder,bridge[N];
Expand All @@ -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]);
}
}
Expand All @@ -75,32 +132,35 @@ void findBridge(int n)
}
}
```
# 边双缩点
把每个极大边双连通分量缩成一个点,原图变成一颗树,树上问题就比较好解决

tarjan访问每个点时把点进栈,当一个点 u 的后续点都访问完时,如果 u 是一个极大边双连通分量的,第一个访问的点,把栈内的点都拿出来,直到 u。同时给原来的点做一个映射。同一个映射的点,在同一个极大边双连通分量。
# 边双连通
没有割边的连通图是边双连通,点的边双连通具有传递性
## 边双连通分量缩点
tarjan访问每个点时把点进栈,当一个点 u 的后续点都访问完时,如果 u 是一个极大边双连通分量的,第一个访问的点,把栈内的点都拿出来,直到 u。同时给原来的点做一个映射。同一个映射的点,在同一个边双
新图可能需要旧图的信息,所以旧图的边信息存两份
```cpp
vector<int> e[N];
struct edge
vector<int> e[N],sta,ne[N];
array<int,2> 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<int> 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])
{
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])
Expand All @@ -114,28 +174,75 @@ void tarjan(int u,int fa)
f[u]=bccnum;
bcc.pop_back();
}
vector<array<int,2>> d[N];
void makeNewMap(int m)
{
tarjan(1,1);
for(int i=0;i<m;++i)
{
u=f[ed[i].u],v=f[ed[i].v];
if(u!=v)
int u=f[ed[i][0]],v=f[ed[i][1]];
if(u==v)
continue;
ne[u].push_back(v);
}
}
```
# 点双连通
没有割点的连通图是点双连通,点的点双连通不具有传递性

除了仅包含两个点一条边的点双外,其他点双都满足:任意两点间都存在至少两条点不重复路径(起点终点除外)

任意一个不是割点的点都只存在于一个点双中,割点也一定属于两个及以上的点双

两个点双至多存在一个公共点——割点
## 点双连通分量缩点
```cpp
vector<int> e[N], sta;
int dfn[N], low[N], dfsOrder;
vector<vector<int>> 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<int> 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<m;++i)
{
e[u].push_back(v);
e[v].push_back(u);
ed[i]={u,v,w};
}
makeNewMap(m);
for (int i = 1; i <= n; ++i)
if (!dfn[i])
{
tarjan(i, -1);
if (e[i].empty())//需去除自环
{
vector<int> tmp;
tmp.push_back(i);
ans.push_back(tmp);
}
}
}
```
# 圆方树
原来的每个点对应一个圆点,每一个点双对应一个方点
对于每一个点双,它对应的方点向这个点双中的每个点连边
如果原图连通,则“圆方树”才是一棵树

0 comments on commit b859010

Please sign in to comment.