#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define maxn 100100
#define maxm 500100
#define inf 0x3f3f3f3f
using namespace std;
struct Edge
{
int next,en;
} ;
Edge edge[maxm];
int head[maxn],cnte;//这个是很普通的边的数据结构啦
int Low[maxn],Dfn[maxn],dfsnum;
int Stack[maxn],inStack[maxn],top;
int Belong[maxn],setnum;
int out[maxn];//这个out数组是这个题目要用到的,跟强连通分量并无关系
//这个就是tar的Dfn和Low数组了
//Dfn表示最早dfs遍历到这个节点是第几步
//Low表示这个节点即其所有的子节点能到达的dfn序最前的是哪个节点
//Belong表示这个节点属于那一个分量,setnum表示分量的个数
void init()
{
memset(head,-1,sizeof(head));
memset(Dfn,-1,sizeof(Dfn));
memset(Belong,0,sizeof(Belong));
memset(inStack,0,sizeof(inStack));
memset(out,0,sizeof(out));
cnte=0;dfsnum=0;setnum=0;top=0;
}
void addedge(int st,int en)
{
cnte++;
edge[cnte].next=head[st];
edge[cnte].en=en;head[st]=cnte;
return;
}
void Tarjan(int root)
{
dfsnum++;
Dfn[root]=dfsnum;
Low[root]=dfsnum;
inStack[root]=1;
top++;Stack[top]=root;
int v;
for (int k=head[root];k!=-1;k=edge[k].next)
{
v=edge[k].en;
if (Dfn[v]==-1)
{
Tarjan(v);
Low[root]=min(Low[root],Low[v]);
}
else if (inStack[v])
Low[root]=min(Low[root],Dfn[v]);
}
if (Dfn[root]==Low[root])
{
setnum++;
do
{
v=Stack[top];
top--;inStack[v]=0;
Belong[v]=setnum;
}
while(top!=0&&v!=root);
}
return;
}
int main()
{
int n,m;
while(~scanf("%d %d",&n,&m))
{
init();
for (int k=1;k<=m;k++)
{
int st,en;
scanf("%d %d",&st,&en);
addedge(st,en);
}
for (int k=1;k<=n;k++)
if (Dfn[k]==-1)
Tarjan(k);
for (int k=1;k<=n;k++)
for (int i=head[k];i!=-1;i=edge[i].next)
if (Belong[k]!=Belong[edge[i].en])
out[Belong[k]]++;
int ans,tot(0);
for (int k=1;k<=setnum;k++)
if (out[k]==0)
{tot++;ans=k;}
for (int k=1;k<=n;k++)
if (Belong[k]==-1)
tot=0;
if (tot==1)
{
int cou(0);
for (int k=1;k<=n;k++)
if (Belong[k]==ans)
cou++;
printf("%d\n",cou);
}
else
printf("0\n");
}
return 0;
}
具体的算法思路来自别人的博客
主要就是当Dfn[root]==Low[root]的时候表明栈底元素到当前元素有且只有一个环,那么这就是一个强连通分量
而且有一个性质那就是如果两个元素u,v在一个强连通分量中,那么我dfs到u的话一定是可以dfs到v的这就保证了强连通分量的正确性