https://www.luogu.org/problem/P2341
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=50010;
int pre[maxn],other[maxn],last[maxn],l;
int n,m;
int dfn[maxn],low[maxn],ans[maxn],st[maxn],belong[maxn],cnt,top,qw;
//dfn->dfs序,low是点上非树边指向的点(拥有最小的dfs序 ),st是一个栈,记录环上的点,belong是点所属于的环
void add(int x,int y)
{
l++;
pre[l]=last[x];
last[x]=l;
other[l]=y;
}
int ru[maxn],chu[maxn];//入度,出度
void dfs(int x)
{
dfn[x]=low[x]=++cnt;//可以知道每个点都指向自己(low)
ru[x]=1;
st[++top]=x;
for(int p=last[x];p;p=pre[p])
{
int v=other[p];
if(!dfn[v])
{
dfs(v);//此时v的信息已经更新完毕
low[x]=min(low[x],low[v]);//用儿子更新父亲
}
else if(ru[v])//儿子不在环上,因为环上的点low是没有意义的
{
low[x]=min(low[x],dfn[v]);
}
}
if(dfn[x]==low[x])//说明这是一个环
{
belong[x]=++qw;//qw是环的个数
while(1)
{
int y=st[top--];
ru[y]=0;
belong[y]=qw;
ans[qw]++;
if(x==y) break;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);//时刻注意是有向边
}
for(int i=1;i<=n;i++)
{
if(!dfn[i]) dfs(i);//此操作是防止图不连通
}
for(int i=1;i<=n;i++)
{
for(int p=last[i];p;p=pre[p])
{
int v=other[p];
if(belong[i]!=belong[v])//实际上可以把一个环看成一个点(子环就是一个点)
{
chu[belong[i]]++;//有向边
}
}
}
int p=0;//因为要所有牛都喜欢,所以只允许一个环的出现
for(int i=1;i<=qw;i++)
{
if(!chu[i])
{
if(p!=0)
{
printf("0\n");
return 0;
}
p=i;
}
}
printf("%d\n",ans[p]);
return 0;
}
/*
tarjan求强连通分量;
这个板子就是在一个有向图中,找到一个所有点直接或间接指向的一个点
(实际上可以是一个环,可以想象一个有向环,上面有许多“枝条”,
这样所有环上的点都直接或间接指向环上的点,然后所有枝条上的点都直接或间接指向环上的点;
那么环上的点就是那些所有点都直接或间接指向的)
*/