1. 強連通分量
1.1. 定義
在有向圖中,選取一個點集 \(S\),若對於 \(S\) 中的任意兩點 \(u, v\),都滿足 \(u\) 可以到達 \(v\),則稱 \(S\) 是強連通的。
強連通分量是圖中一個極大的強連通的點集。
性質:把一個有向圖透過強連通分量縮點後,新的圖是一個 DAG.
1.2. Kosaraju 演算法
在無向圖中,求解連通分量只需要按照 \(1\) 到 \(n\) 的順序依次考慮每個點。
考慮到 \(i\) 點時,如果 \(i\) 點未被訪問過,就說明找到了一個新的連通分量,從 \(i\) 點開始 DFS 即可找到i所在的連通分量。
在有向圖中,上述演算法不成立。
假設 \(A\) 和 \(B\) 是兩個不同的強連通分量,且 \(A\) 可以到達 \(B\),那麼只有先訪問 \(B\),再訪問 \(A\),才能使得上述演算法成立。
也就是說,遍歷順序要保證 \(B\) 中至少有一個點排在所有 \(A\) 中的點之前。
考慮縮點後得到的 DAG,每次應訪問出度為 \(0\) 的點,即按照拓撲序的逆序訪問縮點後的所有節點。
Kosaraju 演算法的思想:
- 把圖中所有邊反向,再按照 \(1\) 到 \(n\) 的順序去DFS,到達每個點時將其入棧,得到每個點的出棧序列 \(q_1, q_2, \ldots, q_n\),即為縮點後的拓撲序.
- 按照 \(q_n,\ldots, q_1\) 的順序去 DFS 原圖就是一個合法的順序。
因為把邊反向與否不改變強連通分量,所以得到一個簡化的演算法:
- 按照 \(1\) 到 \(n\) 的順序去 DFS 原圖,得到每個點的出棧序列 \(q\).
- 按照 \(q_n\ldots q_1\) 的順序去 DFS 反圖,依次得到每個強連通分量。
#include <bits/stdc++.h>
using namespace std;
bool vis[N];
vector<int> g[2][N];
stack<int> stk;
void dfs1(int u)
{
vis[u] = 1;
for(auto v : g[0][u])
if(!vis[v]) dfs1(v);
q.push(x);
}
void dfs2(int u, int id)
{
vis[u] = 0, bel[u] = id;
for(auto v : g[1][u])
if(vis[v]) dfs2(v, root);
}
int main()
{
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
for(int i = 1; i <= n; i++)
if(!vis[i]) dfs1(i);
while(!stk.empty())
{
int u = stk.top(); stk.pop();
if(vis[u]) dfs2(u, ++idx);
}
return 0;
}
2. DFS 樹
一張圖的 DFS 樹是在對其進行 DFS 時,所經過的所有有效邊形成的樹結構。
在構造題中,通常我們用到的是無向圖的 DFS 樹。
無向圖的 DFS 樹滿足所有非樹邊都是返祖邊。
3. 邊雙連通分量
3.1. 定義&性質
一個無向連通圖邊雙連通,當且僅當對於任意兩個點,存在兩條不相交的路徑。
邊雙連通分量是圖中一個極大的邊雙連通的點集。
定義割邊為:在無向圖中,該邊所在連通塊不再連通的邊。
邊雙連通的定義中,“存在兩條不相交的路徑”顯然等價於“如果無論刪去哪條邊都不能使它們不連通”,故一個無向連通圖邊雙連通的充要條件是沒有割邊。
一個無向圖邊雙縮點後是一棵樹,所有樹邊是割邊。
(若縮點後不是一顆樹,說明還存在環,則環上所有的點在一個邊雙內,這與邊雙連通分量“極大”的定義矛盾。)
3.2. 演算法解析
由【3.1. 定義&性質】,求出所有邊雙連通分量只需求出所有割邊,由割邊斷開後得到的一個連通塊就是一個邊雙連通分量。
取原圖的 DFS 樹,只有樹邊有可能是割邊。
一條非樹邊(由【2. DFS 樹】的性質知,只可能是返祖邊)會覆蓋掉一條鏈上的樹邊,這些樹邊就會被 ban 掉,使用樹上差分(或樹剖)維護,剩下的邊就是割邊。
4. Boruvka 演算法
一個神秘的 MST 求法。
維護一些連通塊(初始都是單點),並進行若干輪連邊。
每一輪中,找到每個連通塊連向其它連通塊的最小邊(需要做不等離散化,以邊權為第一關鍵字,以邊的編號為第二關鍵字)並連上,這一條邊一定會在 MST 中。
(可以根據 Kruscal 的流程證明。)
每輪連通塊數量至少減半,最多進行 \(O(\log n)\) 輪連邊。
最劣時間複雜度 \(O((n + m) \log n)\).
Boruvka 演算法適合用來求完全圖(或非常稠密圖)的 MST.
5. 例題
5.1. CF555E Case of Computer Network
給定一張 \(n\) 個點 \(m\) 條邊的無向圖和 \(q\) 組有向點對 \((s, t)\)。
詢問是否存在使得所有 \(s\) 都能到達 \(t\) 的無向圖中每條邊的定向方案。
\(n,m,q \le 2 \times 10^5\).
有一個顯然的結論:
在邊雙連通分量中可定向,使得內部構成強連通。
只需取邊雙中的 DFS 樹,使樹邊向下,非樹邊向上即可。
則在本題中,若 \(s, t\) 在同一邊雙,則不必考慮(一定可以)。
那麼只需考慮 \(s, t\) 在不同邊雙的情況。
考慮將原圖按邊雙縮點成一顆樹。
對於每個詢問 \(s, t\),在樹上的路徑一定為 \(s\) 到根之間向上、根到 \(t\) 之間向下。
可以使用樹鏈剖分+線段樹區間覆蓋來做,做的過程中 check 當前要定向的邊中是否有邊已經被定了相反的方向。
5.2. CF1364D Ehab's Last Corollary
給定一個無向連通圖和正整數 \(k\),求一個大小為 \(\lceil \frac{k}{2} \rceil\) 的獨立集或大小不超過 \(k\) 的環(求出任意一個即可)。
首先找到圖中的最小環。
- 若環的大小不超過 \(k\),則已經得到了解。
- 若環的大小大於 \(k\):對環進行黑白染色,由狄利克雷抽屜原理,必然存在一個顏色全部相同的大小大於 \(\lceil \frac{k}{2} \rceil\) 的獨立集,從其中任選 \(k\) 個點即可。
問題轉化為如何求最小環。
對於一條非樹邊 \((u, v)\),定義 \(w_{u, v} = |dep_v - dep_u|\).
找到圖中 \(w\) 最小的非樹邊,它和 \(u, v\) 之間的所有樹邊構成最小環。
5.3. CF1103C Johnny Solving
給定一張 \(n\) 個點 \(m\) 條邊的無向連通圖,以及一個整數 \(k\),保證每個點度數 \(\ge 3\),你需要找到一個至少有 \(\lceil \frac{n}{k} \rceil\) 個點的路徑,或者找出 \(k\) 個環,其中每個環的環長都不是 \(3\) 的倍數,且每個環中至少有一個點在這 \(k\) 個環裡只出現一次。
任取一棵 DFS 樹。
- 如果最大深度 \(\lceil \frac{n}{k} \rceil\),則找到了路徑。
- 否則,根據樹的性質,至少有 \(k\) 個葉子。
由題中“保證每個點度數 \(\ge 3\)”的限制知,對於每個葉子,其至少有兩條返祖邊。
任取其中兩條,根據模 \(3\) 的餘數分討後不難發現,一定存在一個包含這個葉子的環,滿足其環長不是 \(3\) 的倍數。
5.4. LG5811 [IOI2019] 景點劃分
給定一個 \(n\) 個點、\(m\) 條邊的無向連通圖,以及三個整數 \(a, b, c\),滿足 \(a + b + c = n\),你需要將 \(n\) 個點分成三個集合 \(A, B, C\),大小分別為 \(a, b, c\),使得其中至少兩個集合是連通的 (集合中的任意兩個點能只經過該集合內的點互相到達),或者報告無解。
不妨設 \(a \le b \le c\),則讓集合 \(A, B\) 連通的限制一定是最松的。
考慮原圖是樹的情況。我們想找到一條邊,讓它兩側的點數分佈儘量均勻。
考慮重心,找到所有以重心為根的子樹中最大的一個,連線這棵子樹與重心的邊即為該邊。有解當且僅當子樹 \(size \ge a\).