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 "); return 0; } p=i; } } printf("%d ",ans[p]); return 0; } /* tarjan求强连通分量; 这个板子就是在一个有向图中,找到一个所有点直接或间接指向的一个点 (实际上可以是一个环,可以想象一个有向环,上面有许多“枝条”, 这样所有环上的点都直接或间接指向环上的点,然后所有枝条上的点都直接或间接指向环上的点; 那么环上的点就是那些所有点都直接或间接指向的) */