10/26

SCC & BCC

Copied from Eric Xiao

關節點

拔掉之後會使整張圖不連通的點

DFS樹

找關節點

因為DFS樹子樹之間不會相連

(會相連就會屬於同一子樹)

 

所以樹根只要有兩個以上的子樹,就是關節點

找關節點

點p的每個子孫都有back edge可以連到p的祖先

該點就不是關節點

 

反之若有一個子孫沒有back edge連到p的祖先,該點就是關節點

實作方式

一邊DFS一邊記錄每個點的low值跟vis值

 

vis值就是DFS順序編號

 

low值:DFS樹中以某個節點為根的子樹

透過走最多一條back edge(tree edge不限)能走到的最小前序編號

int cnt = 0;
vector<int> G[MAXN];
int vis[MAXN], low[MAXN], iscut[MAXN];
void DFS(int u, int p) {
    low[u] = vis[u] = ++cnt; // low至多是自己
    int child = 0;
    for(auto v : G[u]) {
        if(!vis[v]) {
            child++;
            DFS(v, u);
            low[u] = min(low[u], low[v]);
            if(low[v] >= vis[u]) iscut[u] = 1; // 有子樹回不去
        }
        else if(vis[v] < vis[u] && v != p) {
            low[u] = min(low[u], vis[v]); // back edge
        }
    }
    if(p <= 0 && child == 1) iscut[u] = 0;
}

拔掉之後會使整張圖不連通的邊

怎麼找?

一樣low值

找橋

若點p有一個子孫沒有back edge能連到p或p的祖先,則p到該子樹的邊就是橋

int cnt = 0;
vector<int> G[MAXN], isbridge[MAXN];
int vis[MAXN], low[MAXN], iscut[MAXN];
void DFS(int u, int p) {
    low[u] = vis[u] = ++cnt;
    int child = 0;
    for(int i = 0;i < G[u].size();i++) {
    	int v = G[u][i];
        if(!vis[v]) {
            child++;
            DFS(v, u);
            low[u] = min(low[u], low[v]);
            if(low[v] > vis[u]) isbridge[u][i] = 1;
            // 沒有等號 因為回的到v的話就切不斷(?)
        }
        else if(vis[v] < vis[u] && v != p) {
            low[u] = min(low[u], vis[v]);
        }
    }
}

BCC

Broadcasting Corporation of China

邊雙連通分量

就是沒有橋的一坨連通的東西QQ

怎麼找?

找邊雙連通分量

很顯然先找到橋

然後拔掉

然後再對整張圖DFS一次

 

或是在DFS的時候不要把橋當邊(?)

BCC(1)

Broadcasting Corporation of China

點雙連通分量

一張圖點雙連通的極大子圖

怎麼找?

找點雙連通分量

每條邊只屬於一個點雙連通分量

 

貌似要用stack維護某些東西

Code by Eric Xiao

SCC

有向圖的強連通分量

was ist SCC

任何點都可以走到任何點的最大子圖

Kosaraju演算法

Step 1.

把原圖的所有邊倒過來

在新圖上DFS,記錄離開每個子點的時間。

Kosaraju演算法

Step 2.

在原圖上越晚離開的先DFS

每次DFS可以走到的節點即屬於同一個SCC。

#include<bits/stdc++.h>
#define int long long
using namespace std;

vector<int> adj[100000];
vector<int> rev[100000];
vector<int> SCC[100000];
int vis[1000000];
vector<int> W;

void dfs1(int n) {
    vis[n] = 1;
    for(auto i: rev[n]) {
        if(!vis[i]) dfs1(i);
    }
    W.push_back(n);
}

int cnt;
void dfs2(int n) {
    vis[n] = 1;
    BCC[cnt].push_back(n);
    for(auto i: adj[n]) {
        if(!vis[i]) dfs2(i);
    }
}

signed main() {
    int n, m;
    cin >> n >> m;
    for(int i=0;i<m;i++) {
        int a, b;
        cin >> a >> b;
        adj[a].push_back(b);
        rev[b].push_back(a);
    }
    for(int i=0;i<n;i++) dfs1(i);
    for(int i=0;i<n;i++) vis[i] = 0;
    reverse(W.begin(), W.end());
    for(auto i: W) {
        dfs2(i), cnt++;
    }
    // 
}

XCC有啥用?

2-SAT

無向圖把B(ridge)CC縮成一點

會變成一棵樹


有向圖把SCC縮成一點

會變成一個DAG

點雙連通分量怎麼縮?

把一個雙連通分量視作一個點,把一個關節點也視作一個點

裸題

找關節點

給一張有向圖,請問最少要多少個起點才能遍歷每個點。

找橋連通分量

我不會的題QQ

好題

Made with Slides.com