Tree Edge:DFS 過程中經過的邊。DFS 時,走向沒走過的臨點的鄰邊都是 Tree Edge。
Back Edge:通向祖先的鄰邊。
Forward Edge:不再 DFS Tree 上,通向子樹的鄰邊。
Cross Edge:通向不存在父子關係的點的鄰邊。
在 DFS 時,遇到走訪過的邊就是下面三種之一。 並且無向圖只存在 Tree Edge 和 Back Edge(Forward Edge 因為無向等同於 Back Edge;u 若存在 Cross Edge 連到 v,那在 v 時就應改把這條邊當成 Tree Edge)。所以討論連通的時候,只需要討論子孫和祖先的關係。
Tarjan's BCC
割點
在 DFS Tree 上當一個點被拔掉,子孫還想要跟祖先點互通有無,那就只能透過 Back Edge 了。 如果這個子樹中有一條 Back Edge 可以回到任一個祖先,那點被拔掉就不會影響該子樹根祖先互通。 反之若其中一個子樹不存在 Back Edge 可以連回祖先,那該點就是割點。
low 函數
我們可以記錄該點的子孫通過 Back Edge 最上面可以連到哪裡,這樣祖先們都能用它該點是否可以走到自己上面。我們稱它為 \(low(u)\)。 當走 Tree Edge 到子節點,可以直接拿他的 \(low\);或是走 Back Edge 到祖先,則拿他的深度。
int low[N], dep[N], n, m, bcnt; vector<int> st, memb[N]; voiddfs(int u, int p){ if (u == p && G[u].size()<1){ memb[bcnt++].pb(u); return; } low[u] = dep[u] = dep[p]+1; st.pb(u); for (auto v: G[u]){ if (v == p) continue; if (dep[v]){ low[u] = min(low[u], dep[v]); } else{ dfs(v,u); low[u] = min(low[u], low[v]); if (low[v] >= dep[u]){ bcnt++; while(1){ memb[bcnt].pb(st.back()); if (st.back() == u) break; st.pop_back(); } } } } }