本文是《劉汝佳算法競賽》的雙連通分量一節的總結
前置名詞講解
- 割點: 若無向圖G中,存在一個點,當該點被刪除時,圖G中的連通分量數目增加。整張圖不再連通。
- 連通分量:若圖G連通,則連通分量便是自身,非連通的無向圖有多個連通分量。、
- 極大連通子圖:對於一個連通圖,極大連通子圖就是其連通分量也就是 其本身,非連通圖有多個連通分量則就有多個極大連通子圖。主要理解的是極大,極大指的是這個連通子圖不能被其他連通子圖所包含。對於一個連通圖來說,只有其本身才能不被其他連通子圖所包含。
- 簡單環:一個環,除了終點和起點是同一點外,其他點在環中只出現一次。
- 點雙連通:任意兩點之間都存在兩條“點不重複的路徑”。若圖G滿足,則圖稱作“點-雙連通”的,這個要求等價於任意兩條邊都在同一簡單環中,即內部無割點;
- 點-雙連通分量:一個子圖滿足點雙連通且在圖G中是極大(在滿足點-雙連通的所有子圖中是極大的)
性質
- 點雙連通分量的任意兩條邊都存在同一簡單環中,有一個例外。
- 圖中的割點都是至少兩個雙連通分量的公共點,而非割點則是雙連通分量的內部點。
- 由上一條性質可知,雙連通分量去掉任意一點,仍然保持連通。
- 不同雙連通分量之間只有一個公共點,當由多個公共點是,則必然被更大的點雙連通子圖所包含
- 一般點雙聯通圖都是簡單環,但下面的圖仍然可以通過點雙連通的測試,大概是兩個點都不是割點的原因。
代碼
#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);
}
}