無向圖上的 Tarjan
定義:
- \(dfn_u\):點 \(u\) 的時間戳,記錄點 \(u\) 是 DFS 過程中被訪問的時間。
- \(low_u\):記錄點 \(u\) 經過至多一條非樹邊,所能達到的 dfn 最小的節點。
對於 DFS 中每一條遍歷到的邊 \((u,v)\):
- 若搜尋樹上 \(v\) 是 \(u\) 的兒子,即邊 \((u,v)\) 是樹邊,則有 \(low_u\gets \min(low_u, low_v)\)。
- 否則邊 \((u,v)\) 一定是返祖邊,即 \(v\) 是 \(u\) 的祖先,則有 \(low_u=\min(low_u,dfn_v)\)。
- 可以發現對無向圖進行 DFS 遍歷時,是不可能存在橫叉邊的。
割點
一個點是割點的充要條件是:
- 若點 \(u\) 為搜尋樹的根,且 \(u\) 有兩個以上的兒子,則 \(u\) 是割點。
- 否則,如果存在 \(u\) 的一個兒子 \(v\),滿足 \(low_v\ge dfn_u\),則 \(u\) 是割點。
點雙連通分量
- DFS 過程中,用棧來儲存被訪問過的點。
- 對於點 \(u\),如果它的兒子 \(v\) 滿足 \(low_v\ge dfn_u\),則 \(u\) 是割點。不斷彈棧,直到彈出 \(v\);此時彈出的點和 \(u\) 就構成了一個點雙。
- DFS 結束後,棧中剩下的點們構成一個點雙。
- 割點會存在於多個點雙之中,其他點以及每一條邊只會存在一個點雙裡。
割邊
- 首先非樹邊不可能成為割邊。
- 對於樹邊 \((u,v)\),其中 \(u\) 是 \(v\) 的父親;如果 \(low_v > dfn_u\),則邊 \((u,v)\) 是割邊。
- 或者如果 \(low_u = dfn_u\),則 \(u\) 和 \(u\) 的父親之間的樹邊是割邊。
- 原理就是如果 \(v\) 無法透過返祖邊和 \(u\) 的祖先聯通,則 \((u,v)\) 是割邊。
邊雙連通分量
- 同樣建立棧,將遍歷到的點依次壓入棧中。
- 如果 \(low_u = dfn_u\),則 \(u\) 和 \(u\) 的父親是割邊;則不斷彈棧,直到彈出點 \(u\)。然後將這些剛彈出的點歸入一個邊雙中。
- 割邊不會存在於任何一個邊雙裡;其餘邊和所有點會恰好存在於一個邊雙裡。
- 容易發現,縮完點後的圖會變成一棵優美的樹。
有向圖上的 Tarjan
定義:
- \(dfn_u\):點 \(u\) 的時間戳,記錄點 \(u\) 是 DFS 過程中被訪問的時間。
- \(low_u\):記錄點 \(u\) 經過至多一條返祖邊,所能達到的 dfn 最小的節點。
強連通分量
定義:
- 如果兩個點 u,v,存在 u 到 v 的路徑,且存在 v 到 u 的路徑,則稱這兩個點強連通。
- 所有點都強連通的圖稱為強連通圖。
- 我們把圖中極大的強連通子圖稱為強連通分量。
求法:
- 同樣開一個棧,將 dfs 到的點依次壓入棧裡面。
- 每次更新 \(low_u\) 時,如果是橫叉邊且那個點無法回到 \(u\) 的某個祖先(即那個點不在棧裡面)則無視。
- 當 \(dfn_u=low_u\) 時,則不斷彈棧,直到彈出 \(u\),然後將這些點歸入一個 SCC 中。
- 縮完點後的圖是個 DAG。