关于连通性问题
连通性问题离不开两个定义的数组,low[]与dfn[],dfn数组存的是节点的dfs序,low[]存的是某个节点不通过父亲节点所能到达的点中dfn值最小的点的dfn值。
1.割点:
割点的定义是指在一个无向图中,若删去该点,则会使原来的一个联通块变为至少两个联通块。
那么我们考虑根据dfn[u]和low[v](v 为 u儿子节点)的关系来判断一个点u是否为割点。
考虑若low[v] >= dfn[u],则说明v无法通过其他路径到达比u dfn序小的节点,也就说明若将u删去,则v及其子树会成为一个新的联通块。符合定义。
2.割边:
割边的定义是指在一个无向图中,若删去该点边,则会使原来的一个联通块变为至少两个联通块。
如上一样考虑,此时考虑若low[v] == dfn[u],那说明v有一条路径可以到达u,若将u删去,依然无法形成新的联通块,因此,将上面的条件改变一下。当low[v] > dfn[u]时,则说明该无向边是割边。
3.点双连通分量:
点双连通分量的定义是指在该连通分量中,一次只删去其中任意一个点,该连通分量依旧连通。
根据定义易知每次遇到割点时,将已遍历的点都作为新的连通分量,那么最终剩下的所有双连通分量都是点双连通分量。
4.边双连通分量:
边双连通分量的定义是指在该连通分量中,一次只删去其中任意一条边,该连通分量依旧连通。
易知将所有割边删去后,剩下的连通分量都是边双连通分量。
代码实现:
割点:
#include<bits/stdc++.h>
using namespace std;
int const maxn = 1e5 + 10;
int cnt;
int cut[maxn];
vector<int> G[maxn];
int tot;
int rt;
int dfn[maxn],low[maxn];
void dfs(int u,int fa){
low[u] = dfn[u] = ++cnt;
int flg = 0;
for(int v : G[u]){
if(!dfn[v]){
dfs(v,u);
low[u] = min(low[u],low[v]);
if(low[v] >= dfn[u]){
flg ++;
if(u!=rt || flg>=2){
if(!cut[u])tot ++;
cut[u] = 1;
}
}
}else if(v!=fa){
low[u] = min(low[u],dfn[v]);
}
}
}
#define pb push_back
signed main(){
int n,m;
cin >> n >> m ;
for(int i = 1;i <= m;i ++){
int x,y;
cin >> x >> y;
G[x].pb(y);
G[y].pb(x);
}
for(int i = 1;i <= n;i ++){
if(!dfn[i]){
rt = i;
dfs(i,i);
}
}
cout << tot << '\n';
for(int i = 1;i <= n;i ++){
if(cut[i])cout <<i<<' ';
}
return 0;
}
边双:
#include<bits/stdc++.h>
using namespace std;
int const maxn = 2e6 + 10,M = 5e5 + 10;
struct edg{
int v,nxt;
}e[maxn*2];
vector<int> ecc[M];
int low[M],dfn[M];
int tot;
int head[M],cnt;
bool vis[M],cut[maxn*2];
void add(int u,int v){
e[++cnt] = {v,head[u]};
head[u] = cnt;
}
int dcnt;
void dfs(int u,int in){
low[u] = dfn[u] = ++dcnt;
for(int i = head[u];i;i = e[i].nxt){
int v = e[i].v;
if(i==(in^1))continue;
if(!dfn[v]){
dfs(v,i);
low[u] = min(low[u],low[v]);
if(low[v] >= dfn[u]){
cut[i] = 1;
cut[i^1] = 1;
}
}else {
low[u] = min(low[u],dfn[v]);
}
}
}
void dfs1(int u,int fa){
vis[u] = 1;
for(int i = head[u];i;i = e[i].nxt){
int v = e[i].v;
if(v==fa||cut[i]||vis[v])continue;
ecc[tot].push_back(v);
dfs1(v,u);
}
}
signed main(){
int n,m;
cnt = 1;
cin >> n >> m ;
for(int i = 1;i <= m;i ++){
int u,v;
cin >>u >> v;
add(u,v);
add(v,u);
}
for(int i = 1;i <= n;i ++){
if(!dfn[i]){
dfs(i,0);
}
}
for(int i = 1;i<= n;i ++){
if(!vis[i]){
ecc[++tot].push_back(i);
dfs1(i,0);
// return 0;
}
}
cout << tot <<'\n';
for(int i = 1;i <= tot;i ++){
cout << ecc[i].size() <<' ';
for(int v : ecc[i]){
cout << v << ' ';
}
cout << '\n';
}
return 0;
}
点双:
#include<bits/stdc++.h>
using namespace std;
int const maxn = 2e6 + 10;
int const M = 5e5 + 10;
int dcnt;
vector<int> G[M],dcc[M];
int dfn[M],low[M];
stack<int> st;
int tot;
int rt;
void dfs(int u,int fa){
dfn[u] = low[u] = ++dcnt;
if(G[u].size()==0){
dcc[++tot].push_back(u);
return ;
}
st.push(u);
int flg = 0;
for(int v : G[u]){
if(v==fa||v==u)continue;
if(!dfn[v]){
dfs(v,u);
low[u] = min(low[u],low[v]);
if(low[v] >= dfn[u]){
flg ++;
int z;
tot ++;
do{
z = st.top();
st.pop();
dcc[tot].push_back(z);
}while(z!=v);
dcc[tot].push_back(u);
}
}else {
low[u] = min(low[u],dfn[v]);
}
}
}
signed main(){
int n,m;
cin >> n >> m ;
for(int i = 1;i <= m;i ++){
int u,v;
cin >> u >> v;
if(u==v)continue;
G[u].push_back(v);
G[v].push_back(u);
}
for(int i = 1;i <= n;i ++){
if(!dfn[i]){
while(st.size())st.pop();
rt = i;
dfs(i,0);
}
}
cout << tot << '\n';
for(int i = 1;i <= tot;i ++){
cout << dcc[i].size() <<' ';
for(int v : dcc[i]){
cout << v <<' ';
}
cout << '\n';
}
return 0;
}

浙公网安备 33010602011771号