强连通分量
强连通分量 : 在有向图中, 几个最大的子集, 集合中任意两个元素都可以互相到达。
DFS 序列在有向图中没有那个性质。
首先你可以像边双连通分量那样统计,
如果直接这样, 6 和 3 不存在祖先关系, 这是, 需要用到一个容器, 栈。
可以发现, 如果这样遍历, 同一颗字数的元素在一起。
如果一个元素的 low = dfn
, 就说明他的字数的儿子无论如何也跳步出去, 这就是一个强连通分量。
及时把栈内的元素弹出。
可以知道, 如果遍历到 6 -> 3
这样的边, 如果 3
还在栈内, 就说明 3
和 lca(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;
}