强连通分量
主流的写法有两种:Kosaraju算法和Tarjan 算法
求完强连通后可以缩点,缩点后图会变成DAG。
Tarjan算法
Kosaraju算法
该算法依靠两次简单的 DFS 实现:
第一次 DFS,选取任意顶点作为起点,遍历所有未访问过的顶点,并在回溯之前给顶点编号,也就是后序遍历。
第二次 DFS,对于反向后的图,以标号最大的顶点作为起点开始 DFS。这样遍历到的顶点集合就是一个强连通分量。对于所有未访问过的结点,选取标号最大的,重复上述过程。
两次 DFS 结束后,强连通分量就找出来了,Kosaraju 算法的时间复杂度为\(\Theta(N+M)\)
c++代码
#define int long long
using namespace std;
const int maxn=1e5+1000;
vector<int>g[maxn],g2[maxn];
vector<int>s;
int vis[maxn],clr[maxn];
int n,m,cnt,dfs_clock,tt;
inline void read(int &x){
char tmp=getchar();x=0;
while(tmp<'0'||tmp>'9')tmp=getchar();
while(tmp>='0'&&tmp<='9')x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar();
}
void dfs1(int u){
vis[u]=1;
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
if(!vis[v])dfs1(v);
}
s.push_back(u);
}
void dfs2(int u){
clr[u]=cnt;
tt++;
for(int i=0;i<g2[u].size();i++){
int v=g2[u][i];
if(!clr[v])dfs2(v);
}
}
signed main(){
int T;cin>>T;
while(T--){
cin>>n>>m;
for(int i=1;i<=n;i++)
g[i].clear(),g2[i].clear();
for(int i=1;i<=m;i++){
int x,y;read(x),read(y);
g[x].push_back(y);
g2[y].push_back(x);
}
memset(vis,0,sizeof(vis));
memset(clr,0,sizeof(clr));
dfs_clock=0,s.clear();
for(int i=1;i<=n;i++)
if(!vis[i])dfs1(i);
cnt=0;int ans=0;
for(int i=n-1;i>=0;i--)
if(!clr[s[i]]){
cnt++;
tt=0;
dfs2(s[i]);
ans+=(tt-1)*tt>>1;
}
cout<<ans<<endl;
}
return 0;
}

浙公网安备 33010602011771号