dfn[u] 表示遍历到u的时间戳
low[u] 从u走,所能遍历到最小的时间戳
stack;访问过但没有归为任何一个强连通分量的节点
假如u是其所在的强连通分量的最高点,则dfu[u]==low[u]
留个眼
受欢迎的牛
#include <bits/stdc++.h>
using namespace std;
const int N=1e4+7,M=5e4+7;
int n,m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp;//当前节点的时间戳 当前节点的子树的时间戳的最小值
int stk[N], top;//栈
bool in_stk[N];//判断节点是否在栈里面
int id[N], scc_cnt;//标记每个节点属于哪个强连通块 当前有多少强连通分量
int din[N], dout[N];//统计强连通块的入度和出度
int size_scc[N];//每个强连通块的大小(强连通分量点的数量)
void add(int a, int b)
{
e[idx] = b; //存储节点的值
ne[idx] = h[a]; //存储节点的next指针
h[a] = idx ++ ; //idx表示当点用到哪个节点
}
void tarjan(int u)
{
dfn[u] = low[u] = ++ timestamp;
stk[ ++ top] = u, in_stk[u] = true;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (!dfn[j])
{
tarjan(j);
low[u] = min(low[u], low[j]);
}
else if (in_stk[j])
low[u] = min(low[u], dfn[j]);
}
if (dfn[u] == low[u])//如果记录的两个时间戳相等,则说明u为强连通块的最高点
{
++ scc_cnt;
int y;
do {
y = stk[top -- ];
in_stk[y] = false;
id[y] = scc_cnt;
size_scc[scc_cnt]++;
} while (y != u);
}
}
int main()
{
cin >>n>>m;
memset(h,-1,sizeof h);
while(m--)
{
int a,b;
cin >>a>>b;
add(a,b);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i])//当前节点未被遍历过时,时间戳为0
tarjan(i);
}
for(int i=1;i<=n;i++)//缩点
{
for(int j=h[i];~j;j=ne[j])
{
int k=e[j];
int a=id[i],b=id[k];//两个节点的强连通块不属于一个时,就建一条a—>b的边
if(a!=b) dout[a]++;
}
}
int cnt=0,ans=0;
for(int i=1;i<=scc_cnt;i++)
{
if(!dout[i])
{
cnt++;
ans+=size_scc[i];
if(cnt>1)
{
ans=0;
break;
}
}
}
cout <<ans<<'\n';
}