强连通分量

主流的写法有两种:Kosaraju算法和Tarjan 算法

求完强连通后可以缩点,缩点后图会变成DAG。

Tarjan算法

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;
}
posted @ 2022-03-14 16:51  xyc1719  阅读(57)  评论(0)    收藏  举报