无向图连通性

概念辨析

点双连通分量(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缩点。

posted @ 2025-02-20 22:40  Luke_li  阅读(18)  评论(0)    收藏  举报