https://www.acwing.com/problem/content/1176/
对于拓扑图 如果某一个点的出度为0 那么这个点就能被其他所有点到达
但是题目里所给的点不是拓扑图 所以我们需要缩点
强联通分量里面的所有点互相到达 外面的点到这个强联通的点可以到达
所以就可以强联通分量上的点就是我们需要的点了
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e4+10,M=5e4+10;
int n,m;
int h[N], e[M], ne[M], idx;
int dfn[N],low[N],timestamp;//low 从u开始走能遍历到的最小时间戳
int stk[N],top;
//不需要建立出缩小完后的点
bool in_stk[N];//表示是不是在栈当中
int id[N],scc_cnt,Size[N];//表示每个点都是哪个强联通分量里面的 有多少个强联通分量 每个强联通分量的大小是
int dout[N];
void add(int a, int b) // 添加一条边a->b
{
e[idx] = b, ne[idx] = h[a], h[a] = 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]);//如果j比自己还小那么说明能走到上面
}
////如果j在栈中 说明还没出栈 而且dfs序比当前点u小 要么这个横叉 要么指回去 两种情况j的dfs序都小 所以要更新
else if(in_stk[j]) low[u]=min(low[u],dfn[j]);//在站里面的
}
if(dfn[u]==low[u])//如果自己就是最上面的点 那么自己就是强联通分量的点
{
++scc_cnt;
int y;
do{
y=stk[top--];//取出栈里面的点
in_stk[y]=false;
id[y]=scc_cnt;
Size[scc_cnt]++;
}while(y!=u);//因为栈中的元素dfs序比u大 所以我们只要吧dfs序打的点pop 当y'u的时候 点u所在的所有强联通分量都标记了id
}
}
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])
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]++;//a走到b
}
}
}
int zeros=0,sum=0;
for (int i = 1; i <= scc_cnt; i ++ ){//对于缩点后的点
if(!dout[i]){//如果出度为0
zeros++;//出度为0的点+1
sum+=Size[i];
if(zeros>1){//题目结论:如果多于一个点的出度为0 那么久不回有任何一个
sum=0;
break;
}
}
}
printf("%d\n",sum);
return 0;
}
标签:里面,几个,++,stk,int,dfn,low,联通,求图
From: https://www.cnblogs.com/liang302/p/16640750.html