强连通分量(Kosaraju)

//P2002解题思路:
//先求SCC,缩点后,转换为DAG(有向无环图)
//在DAG上统计入度为0的scc数量即可

//Kosaraju时间复杂度:O(N+E)
//两次DFS,2N,图的转置E,共2N+E 
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=500010;
struct edge{ int t; edge *nxt; edge(int to, edge * next){ t=to, nxt=next; } };
edge * h1[maxn], * h2[maxn];											//h2是反图 
void add1(int u, int v){ h1[u]=new edge(v, h1[u]); }
void add2(int u, int v){ h2[u]=new edge(v, h2[u]); }
int n, m, v[maxn], st[maxn], st_k, sccno[maxn], scc_cnt, scc_indegree[maxn];

void dfs1(int x)
{
	v[x]=1;
	for(edge * p=h1[x]; p; p=p->nxt)	if(!v[p->t]) dfs1(p->t);
	st[++st_k]=x;
}

void dfs2(int x)
{
	v[x]=1;
	sccno[x]=scc_cnt;
	for(edge * p=h2[x]; p; p=p->nxt)	if(!v[p->t]) dfs2(p->t);
}

void kosaraju()
{
	for(int i=1; i<=n; i++)	if(!v[i]) dfs1(i);
	memset(v, 0, sizeof(v));
	for(int i=st_k; i>=1; i--)
		if(!v[st[i]]) scc_cnt++, dfs2(st[i]);
}

int main()
{
	scanf("%d%d", &n, &m);
	for(int i=1, b, e; i<=m; i++)
	{
		scanf("%d%d", &b, &e);
		if(b!=e)	add1(b, e), add2(e, b); 							//去除自环 
	}
	kosaraju();
	for(int i=1; i<=n; i++)												//统计每个scc的入度 
		for(edge *p=h1[i]; p; p=p->nxt)
			if(sccno[i]!=sccno[p->t])	scc_indegree[sccno[p->t]]++;	//起点和终点不在一个scc中才统计入度 
	int ans=0; 
	for(int i=1; i<=scc_cnt; i++)		if(!scc_indegree[i]) ans++;		//统计入度为0的scc的个数 
	printf("%d\n", ans);
	return 0;
}
posted @ 2019-03-28 18:06  LFYZOI题解  阅读(294)  评论(0编辑  收藏  举报