雙連通分量(點雙連通分量)

本文是《劉汝佳算法競賽》的雙連通分量一節的總結

前置名詞講解

  • 割點: 若無向圖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);
    }
}

 

 

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