D14【模板】强连通分量 Tarjan 算法
D14【模板】强连通分量 Tarjan 算法_哔哩哔哩_bilibili
强连通:若一张有向图的节点两两互相可达,则称这张图是强连通的
强连通分量 (Strongly Connected Components,SCC):极大的强连通子图
图中的 SCC 有 3 个: {1,2,3,4,9}, {5,6,8}, {7}
对图深搜时,每一个节点只访问一次,被访问过的节点与边构成一颗 搜索树

有向边按访问情况分 4 类:
- 树边 (tree edge):访问节点走过的边。图中的黑色边
- 返祖边 (back edge):指向祖先节点的边。图中的红色边
- 横叉边 (cross edge):右子树指向左子树的边。图中的绿色边
- 前向边 (forward edge):指向子树中节点的边。图中的蓝色边
返祖边与树边必构成环,横叉边可能与树边构成环, 前向边无用

如果节点 x 是某个强连通分量在搜索树中遇到的第一个节点,那么这个强连通分量的其余节点肯定是在搜索树中以 x 为根的子树中
节点 x 被称为这个强连通分量的根
枚举 x 的邻点 y,分三种情况:
- 若 y 尚未访问:对 y 深搜。回 x时,用 low[y] 更新 low[x]。因为 x 是 y 的父节点,y 能访问到的点,x 一定也能访问到
- 若 y 已访问且在栈中:说明 y 是祖先节点或左子树节点,用 dfn[y] 更新 low[x]
- 若 y 已访问且不在栈中:说明 y 已搜索完毕,其所在连通分量已被处理,所以不用对其做操作
注意:有向有环图缩点后变成 DAG,缩点的编号是拓扑序的逆序,起点的编号最大,终点的编号最小
P2863 [USACO06JAN] The Cow Prom S - 洛谷
// Tarjan算法 O(n+m) #include<bits/stdc++.h> using namespace std; const int N=10010; int n,m,ans; vector<int> e[N]; int dfn[N],low[N],tim,stk[N],top,scc[N],siz[N],cnt; void tarjan(int u){ dfn[u]=low[u]=++tim; stk[++top]=u; for(int v:e[u]){ if(!dfn[v]){ //若v尚未访问 tarjan(v); low[u]=min(low[u],low[v]); } else if(!scc[v]) //若v已访问且未构成SCC low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]){ //若u不是SCC的根,则low<dfn ++cnt; for(int v=-1;v!=u;){ v=stk[top--]; scc[v]=cnt; //SCC的编号 ++siz[cnt]; //SCC的大小 } } } int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>n>>m; for(int a,b;m--;) cin>>a>>b,e[a].push_back(b); for(int i=1;i<=n;i++)if(!dfn[i]) tarjan(i); for(int i=1;i<=cnt;i++)if(siz[i]>1) ans++; cout<<ans; }
B3609 [图论与代数结构 701] 强连通分量 - 洛谷
浙公网安备 33010602011771号