连通:图中任意两个点可以相互抵达。
强连通:有向图中任意两个点可以相互抵达。
而一张图中的子图如果联通,我们称之为该图的强连通分量。
#include<bits/stdc++.h>
using namespace std;
const int maxn=10005;
struct dot {
int next[maxn],cd;
}a[maxn];
int n,m,sum,tim[maxn],low[maxn],t,tip[maxn];
//tim记录点的访问时间时间,low表示点通过图可以回溯到的点的访问时间中的最小值,tip标记元素是否在栈中,t表示时间
stack<int> k;
int minn(int p,int q) {
if(p <= q) return p;
else return q;
}
void dfs(int z) {
t++;
k.push(z);
tip[z]=1;
tim[z]=low[z]=t; //能参与深搜的点必然是还未被访问的节点
if(a[z].cd != 0) {
for(int i=1;i<=a[z].cd;i++) { //先遍历其所有分支
if(tim[a[z].next[i]] == 0) {
dfs(a[z].next[i]);
low[z]=minn(low[z],low[a[z].next[i]]);
}
else if(tip[a[z].next[i]] == 1) {
//如果其分支节点已经在栈中,说明这两个点必然在同一个强连通分量中
low[z]=minn(low[z],low[a[z].next[i]]);
}
}
}
if(tim[z] == low[z]) {
//如果对z点的子树完成遍历后,low=tim,说明z点无法到达更早的节点,故z点为一个强连通分量的根节点
while(k.top() != z) {
tip[k.top()]=0;
k.pop();
}
tip[z]=0;
k.pop();
sum++;
}
}
int main() {
cin>>n>>m;
for(int i=1;i<=m;i++) {
int x,y;
cin>>x>>y;
a[x].cd++;
a[x].next[a[x].cd]=y;
}
for(int i=1;i<=n;i++) {
if(tim[i] == 0) dfs(i);
}
cout<<sum;
return 0;
}