无向图连通性
概念辨析
点双连通分量(v-DCC)内部没有割点。(割点:删去该点之后子图不连通)
边双连通分量(e-DCC)内部没有割边。(割边:删去该边之后子图不连通)
e-DCC不一定是v-DCC(反例容易举出)。
v-DCC不一定是e-DCC(如一条孤立边)。
点双连通分量(v-DCC)
定义
点双连通分量是极大的互相点双连通的点构成的子图(分量)。在一个图中,割点同时位于两个及以上的v-DCC中,而非割点位于一个v-DCC中。
模板
#include <iostream>
#include <vector>
#include <cstring>
#include <queue>
#include <algorithm>
#include <stack>
#define re register
using namespace std;
typedef long long ll;
const ll N=5e5+10;
ll n,m;
vector<ll> a[N];
ll dfn[N],low[N],dfc,cnt;
stack<ll> st;
vector<ll> ans[N];
void tarjan(ll x,bool root=0)
{
dfn[x]=low[x]=++dfc;
st.push(x);
ll son=0;
for(auto to:a[x])
{
if(!dfn[to])
{
son++;
tarjan(to);
low[x]=min(low[x],low[to]);
if(low[to]==dfn[x])
{
cnt++;
while(st.top()!=to)
{
ans[cnt].push_back(st.top());
st.pop();
}
ans[cnt].push_back(st.top());
st.pop();
ans[cnt].push_back(x);//割点也算点双连通分量的一部分
}
}
else
low[x]=min(low[x],dfn[to]);//理论上要判断不是树边才更新的,但是无所谓
}
if(root && son==0)//判断是否是孤立点
ans[++cnt]={x};
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(ll i=1;i<=m;i++)
{
ll u,v;
cin>>u>>v;
a[u].push_back(v);
a[v].push_back(u);
}
for(ll i=1;i<=n;i++)
{
if(!dfn[i])
tarjan(i,1);
}
cout<<cnt<<endl;
for(ll i=1;i<=cnt;i++)
{
cout<<ans[i].size()<<" ";
for(auto x:ans[i])
cout<<x<<" ";
cout<<"\n";
}
return 0;
}
圆方树
原图中的点被称为圆点。删去原图中的边,然后把一个点双连通分量中的点向一个新的方点连一条边。这样,借由割点的连接,整个图成为了一颗由方点和圆点生成的树,这就是圆方树。圆方树是二分图。
圆方树是树的证明因过于trival所以略。
两点在圆方树上经过的路径上的圆点,就是原图中两点路径必定会经过的点。这是显然的。
边双连通分量(e-DCC)
模板
#include <iostream>
#include <vector>
#include <cstring>
#include <queue>
#include <algorithm>
#include <stack>
#define re register
using namespace std;
typedef long long ll;
const ll N=5e5+10;
ll n,m;
vector<ll> a[N];
ll dfn[N],low[N],dfc,cnt;
stack<ll> st;
vector<ll> ans[N];
void tarjan(ll x,ll fa)
{
dfn[x]=low[x]=++dfc;
st.push(x);
bool tof=0;
for(auto to:a[x])
{
if(!dfn[to])
{
tarjan(to,x);
low[x]=min(low[x],low[to]);
if(low[to]>=dfn[to])
{
cnt++;
while(st.top()!=to)
{
ans[cnt].push_back(st.top());
st.pop();
}
ans[cnt].push_back(st.top());
st.pop();
}
}
else
{
if(to!=fa || tof)//判断是不是父向边,如果不如此做low[x]永远是dfn[fa] low[x]=min(low[x],dfn[to]);
if(to==fa && !tof)
tof=1;
}
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(ll i=1;i<=m;i++)
{
ll u,v;
cin>>u>>v;
if(u!=v)
{
a[u].push_back(v);
a[v].push_back(u);
}
}
for(ll i=1;i<=n;i++)
{
if(!dfn[i])
{
tarjan(i,0);
cnt++;
while(!st.empty())
{
ans[cnt].push_back(st.top());
st.pop();
}
}
}
cout<<cnt<<endl;
for(ll i=1;i<=cnt;i++)
{
cout<<ans[i].size()<<" ";
for(auto x:ans[i])
cout<<x<<" ";
cout<<"\n";
}
return 0;
}
缩点
把e-DCC缩成一个点之后,这个图就变成了树。类似SCC缩点。

浙公网安备 33010602011771号