强连通分量

DSF森林

树边:dfs森林上的边
前向边:祖先->后代的非树边
返祖边:后代->祖先的非树边
横插边:后面的分支连接到前面的分支的边

强连通分量

强连通分量:任意两个点相互可达,也就是存在一个环连接了所有的点
u和v强连通,v和w强连通,所以u,v,w三个是强连通的

Tarjan

#include<bits/stdc++.h>

using namespace std;
const int N = 1e5 + 10;
vector<int> e[N];
vector< vector<int> > scc;
int dfn[N], low[N], idx, belong[N], ins[N], cnt;
stack<int> s;
int n, m;

void dfs(int u){
    dfn[u] = low[u] = ++idx;
    s.push(u); ins[u] = 1;

    for(auto v : e[u]){
        if(!dfn[v]) dfs(v);
        if(ins[v]) low[u] = min(low[u], low[v]);
    }

    if(low[u] == dfn[u]){
        vector<int> c;
        cnt ++;
        while(true){
            int v = s.top();
            s.pop();
            ins[v] = 0;
            c.push_back(v);
            belong[v] = cnt;
            if(u == v) break;
        }
        sort(c.begin(), c.end());
        scc.push_back(c);
    }
}

int main(){
    scanf("%d %d", &n, &m);

    for(int i = 0; i < m; i ++){
        int u, v; scanf("%d %d", &u, &v);
        e[u].push_back(v);
    }

    for(int i = 1; i <= n; i ++){
        if(!dfn[i]) dfs(i);
    }

    sort(scc.begin(), scc.end());

    for(int i = 0; i < scc.size(); i ++){
        for(auto res : scc[i]) printf("%d ", res);
        puts("");
    }
}

Kosaraju

// 先dfs一遍,得到一个出栈顺序
// 重要:有向无环图出栈顺序是反图的拓扑序
// 一个任意有向图,scc缩成一个点,形成一个拓扑序
// 所以最后一个出栈的一定是源点
// 出栈顺序逆序遍历,每次从未分配点开始遍历反图
// 第一个出栈的不一定是汇点
// 可以用位运算优化
#include<bits/stdc++.h>

using namespace std;
const int N = 1e5 + 10;
vector<int> e[N], erev[N];
vector< vector<int> > scc;
vector< int > out, c;
int vis[N];
int n, m;

void dfs1(int u){
    vis[u] = 1;
    for(auto v : e[u]){
        if(!vis[v]) dfs1(v);
    }
    out.push_back(u);
}

void dfs2(int u){
    vis[u] = 1;
    for(auto v : erev[u]){
        if(!vis[v]) dfs2(v);
    }
    c.push_back(u);
}

int main(){
    scanf("%d %d", &n, &m);

    for(int i = 0; i < m; i ++){
        int u, v; scanf("%d %d", &u, &v);
        e[u].push_back(v);
        erev[v].push_back(u);
    }

    for(int i = 1; i <= n; i ++){
        if(!vis[i]) dfs1(i);
    }
    reverse(out.begin(), out.end());
    memset(vis, 0, sizeof vis);
    for(int i = 0; i < out.size(); i ++){
        if(!vis[out[i]]){
            c.clear();
            dfs2(out[i]);
            sort(c.begin(), c.end());
            scc.push_back(c);
        }
    }

    sort(scc.begin(), scc.end());
    for(int i = 0; i < scc.size(); i ++){
        for(auto res : scc[i]) printf("%d ", res);
        puts("");
    }
}

SCC缩点和DP

posted @ 2022-06-21 11:00  牛佳文  阅读(54)  评论(0编辑  收藏  举报