双连通分量(点双连通分量)

本文是《刘汝佳算法竞赛》的双连通分量一节的总结

前置名词讲解

  • 割点: 若无向图G中,存在一个点,当该点被删除时,图G中的连通分量数目增加。整张图不再连通。
  • 连通分量:若图G连通,则连通分量便是自身,非连通的无向图有多个连通分量。、
  • 极大连通子图:对于一个连通图,极大连通子图就是其连通分量也就是 其本身,非连通图有多个连通分量则就有多个极大连通子图。主要理解的是极大,极大指的是这个连通子图不能被其他连通子图所包含。对于一个连通图来说,只有其本身才能不被其他连通子图所包含。
  • 简单环:一个环,除了终点和起点是同一点外,其他点在环中只出现一次。
  • 点双连通:任意两点之间都存在两条“点不重复的路径”。若图G满足,则图称作“点-双连通”的,这个要求等价于任意两条边都在同一简单环中,即内部无割点;
  • 点-双连通分量:一个子图满足点双连通且在图G中是极大(在满足点-双连通的所有子图中是极大的)

性质

  1. 点双连通分量的任意两条边都存在同一简单环中,有一个例外。
  2. 图中的割点都是至少两个双连通分量的公共点,而非割点则是双连通分量的内部点。
  3. 由上一条性质可知,双连通分量去掉任意一点,仍然保持连通。
  4. 不同双连通分量之间只有一个公共点,当由多个公共点是,则必然被更大的点双连通子图所包含
  5. 一般点双联通图都是简单环,但下面的图仍然可以通过点双连通的测试,大概是两个点都不是割点的原因。

代码 

#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<string>
#include<cstring>
#include<cstdio>
#include<stack>
//#include<map>
#pragma warning(disable:4996)
using namespace std;

int n, m;

struct Edge
{
    int u, v;
    Edge(int _u, int _v) :u(_u), v(_v){};
};
const int maxn = 300005;
const int mod = 998244353;
//从左至右,分别是顶点的dfs标记,割点标记,所属的bcc编号,当前的递归深度,当前的bcc编号
int pre[maxn], iscut[maxn], bccno[maxn], dfs_clock, bcc_cnt;
//图G,点双连通图
vector<int>G[maxn], bcc[maxn];
//存储遍历的边
stack<Edge>s;

int dfs(int u, int fa)
{
//lowu用于存储与该点直接相连的最远祖先(最小递归深度)
    int lowu = pre[u] = ++dfs_clock;
    int child = 0;
    for (int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        Edge e = Edge(u, v);
        if (!pre[v])
        {
            s.push(e);
            child++;
            int lowv = dfs(v, u);
            lowu = min(lowu, lowv);
            //若该点的所有子孙节点的最远祖先都比其小,则该点就是割点
            if (lowv >= pre[u])
            {
                iscut[u] = true;
                bcc_cnt++;
                bcc[bcc_cnt].clear();
                int iswire = 0;
                while (1)
                {
                    //以割点为标记,stack中保存的边就是一个点双连通分量。
                    Edge x = s.top(); s.pop();
                    iswire++;
                    if (bccno[x.u] != bcc_cnt)
                    {
                        bcc[bcc_cnt].push_back(x.u);
                        bccno[x.u] = bcc_cnt;
                    }
                    if (bccno[x.v] != bcc_cnt)
                    {
                        bcc[bcc_cnt].push_back(x.v);
                        bccno[x.v] = bcc_cnt;
                    }
                    if (x.u == u&&x.v == v)break;
                }
                if (iswire == bcc[bcc_cnt].size())
                   //当点双连通图中的点和边相等时,便是简单环
            }

        }
        //当其邻接点是深度较低的点,则比较lowu    
        else if (pre[v] < pre[u] && v != fa)
        {
            s.push(e);
            lowu = min(lowu, pre[v]);

        }
    }
    //只有当父节点拥有多个子节点时,才可以是割点。
    if (fa < 0 && child == 1)
        iscut[u] = 0;
    return lowu;
}

void find_bcc(int n)
{
    wire = 0;
    memset(pre, 0, sizeof(pre));
    memset(iscut, 0, sizeof(iscut));
    memset(bccno, 0, sizeof(bccno));
    dfs_clock = bcc_cnt = 0;
    for (int i = 1; i <= n; i++)
    {
        if (!pre[i])dfs(i, -1);
    }
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章