强连通分量

强连通分量 : 在有向图中, 几个最大的子集, 集合中任意两个元素都可以互相到达。

DFS 序列在有向图中没有那个性质。

首先你可以像边双连通分量那样统计,

如果直接这样, 6 和 3 不存在祖先关系, 这是, 需要用到一个容器, 栈。

可以发现, 如果这样遍历, 同一颗字数的元素在一起。

如果一个元素的 low = dfn, 就说明他的字数的儿子无论如何也跳步出去, 这就是一个强连通分量。

及时把栈内的元素弹出。

可以知道, 如果遍历到 6 -> 3 这样的边, 如果 3 还在栈内, 就说明 3lca(3, 6) 在同一个强连通分量里, 6 及它父亲到 lca(3, 6) 也都在, 因为 6 -> 3, 3 -> lca(3, 6), lca(3, 6) -> 6 够成了一个环。

#include<bits/stdc++.h>

using namespace std;

const int N = 5e5 + 5;

struct Node{
  int a, b;
};

vector<Node>g[N];

int n, m, dfn[N], low[N], top, stk[N], cnt, u, v, color[N], tot;

void tarjan(int x, int fa){
  dfn[x] = low[x] = ++cnt, stk[++top] = x;
  for(auto [v, w] : g[x]){
    if(w != fa){
      if(!dfn[v]){
        tarjan(v, w);
        low[x] = min(low[x], low[v]);
      }
      else if(!color[v]){
        low[x] = min(low[x], dfn[v]);
      }
    }
  }
  if(low[x] == dfn[x]){
    ++tot;
    for(; dfn[stk[top]] >= dfn[x]; color[stk[top--]] = tot){
    }
  }
}

int main(){
  cin >> n >> m;
  for(int i = 1; i <= m; ++i){
    cin >> u >> v;
    g[u].push_back({v, i});
  }
  for(int i = 1; i <= n; ++i){
    if(!dfn[i]){
      tarjan(i, 0);
    }
  }
  cout << tot << '\n';
  for(int i = 1; i <= n; ++i){
    cout << color[i] << ' ';
  }
  return 0;
}

posted @ 2024-04-11 17:41  liuyichen  阅读(6)  评论(0编辑  收藏  举报