<style type="text/css"> .slides { text-align: left !important; } </style> # 強連通分量 #### Author:H1de_on_bruH ---- ## 概要 定義 Tarjan演算法 Kosaraju's演算法 --- ### 給定一個有向圖 求有幾個強連通分量 強連通分量(簡稱SCC:Strongly Connected Components)的意思是: **這個子圖裡面任意兩點都可以找到路互通** ![](https://i.imgur.com/M6QjbTx.png =400x) ---- ### 怎麼做? 直接照定義去爆搜複雜度會達到$O(N^2+M)$ 這看起來非常不健康 --- ### 想法1: 試著把這個有向圖想像成一棵有向樹 接者呢? ~~破梗囉~~ ---- ### Tarjan演算法 ---- ![](https://i.imgur.com/dBMMnsy.png =450x) 考慮這個圖 我們可以看到有兩個比較特殊的邊 $3 \rightarrow1$跟$4\rightarrow3$ ---- 藍色那條我們稱為返祖邊 表示從這個邊可以返回該節點的祖先 紅色的叫橫插邊 表示可以尋訪到之前尋訪過的節點 還有另外一種叫做前向邊 表示會尋訪到自己之前就尋訪過的節點 也就是返祖邊反過來 ---- 我們考慮這個跟SCC的關係 如果節點$u$是某個SCC在DFS時遇到的第一個節點 那這個SCC其餘的點都必在以$u$為根的搜索樹子樹中 我們稱$u$為這個SCC的根 :::spoiler 反證法 假設有個節點$v$在這個SCC裡面卻不在$u$的子樹中 那$u$到$v$的路徑中必定有一條離開子樹的邊 這樣只有可能是返祖邊或是橫插邊 然而這兩種邊所指向的節點必是已經被尋訪過的 這就跟$u$是這個SCC的根矛盾 故得證# ::: ---- #### Tarjan實現 在每個節點維護下列幾個值 * $dfn_i$:DFS時節點$i$被搜到的編號 * $low_i$:定義為其子樹以下節點的$dfn$最小值 ---- 做DFS時 若在$u$點時有邊指向$v$ 則考慮下列三種狀況 * $v$未被尋訪過:對他尋訪下去後回溯時用$low_v$去更新$low_u$ * $v$被訪問過 且在stack中:即表示$v$為$u$之祖先節點 利用$dfn_v$更新$low_u$ * $v$被訪問過 且不在stack中:表示$v$所在之SCC已被處理 不做任何更新 [sample code](https://pastebin.com/SAxTYWVH) --- ### 想法2: 走得過去也走得回來 :thinking_face: 那做兩次DFS是不是就可以了? ~~又 破梗囉~~ ---- ### Kosaraju's演算法 aka 兩次DFS ---- 同樣的圖 我們考慮它正反圖的關係 ![](https://i.imgur.com/pY2kPWh.png =300x) ![](https://i.imgur.com/s01F0Sp.png =300x) ---- 若拿在原圖時的一個SCC 若點$u$為第一次DFS時該SCC中最後一個遍歷到的 那點$u$必可以做這個SCC在反圖的根 ---- #### 實作 我們先用DFS遍歷原圖一次 並記錄下各個點的順序 之後再把這個順序倒過來 依照這個倒過來的順序去DFS ```cpp= for(int i=1;i<=n;i++)if(vis[i]==0)dfs1(i); reverse(ts.begin(),ts.end());//ts代表剛剛dfs1走過的順序 for(auto i:ts)if(vis[i])dfs2(i); ``` ---- 完整版請參考下方連結 [sample code](https://pastebin.com/nDcPpDxi) --- ### 例題 [CF:427C](https://codeforces.com/contest/427/problem/C) 裸到吐的裸題 [CSES:Giant pizza](https://cses.fi/problemset/task/1684/) ~~噁心實作,P_L超噁~~:face_vomiting: ---- 比起Tarjan Kosaraju的泛用性比較低 只能拿來求SCC(Tarjan還可以求割點跟橋) 複雜度上來講Tarjan常數也比較小 不過Kosaraju的實作簡單 有能力的話建議都多熟悉 ---- # {謝謝各位的聽講|夕張超可愛的對吧} ![](https://i.imgur.com/U90JZaG.jpg =900x)
{"metaMigratedAt":"2023-06-16T13:04:08.935Z","metaMigratedFrom":"YAML","title":"強連通分量","breaks":true,"slideOptions":"{\"theme\":\"black\",\"transition\":\"slide\"}","contributors":"[{\"id\":\"b6728096-4c9a-4b93-9b51-70c56850e20f\",\"add\":2476,\"del\":353}]"}
    1573 views
   Owned this note