传送门:受欢迎的牛
思路:一个很基本的思路就是,看一个点是否从所有点都能走到它,建立一个反向图,从每一个节点出发做一遍spfa,如果能到达其他的所有节点,说明该节点符合要求。但是这种做法的时间复杂度是是O(n*(n+m)),所以不可取。
改用强连通分量的拓扑图来解决问题:
对于由强连通分量编号形成的拓扑图中,如果有答案的话,那么就一定会有一个点的出度为零,如果有两个点的出度为0的话,因为拓扑图的性质,从其中一个点是无论如何也走不到另一个节点的,所以答案为0.答案不为0则答案就是出度为0的编号的强连通分量里面包含的所有点。
因为强连通分量里面的所有点都是可以互相到达的,而其他分量外的所有点都可以到达这个强连通分量。
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
const int N=1e4+10,M=5e4+10;
int e[M],ne[M],h[N],w[N],idx;
int dfn[N],low[N],times;
int stk[N],top;
bool in_stk[N];
int id[N],scc_cnt,cnt[N];
int dout[N];
int n,m;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u)
{
dfn[u]=low[u]=++times;
stk[++top]=u;
in_stk[u]=true;
for(int i=h[u];i!=-1;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])
{
++scc_cnt;
int y;
do
{
y=stk[top--];
in_stk[y]=false;
id[y]=scc_cnt;
cnt[scc_cnt]++;
}while(y!=u);
}
}
int main()
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);
while(m--)
{
int a,b;
scanf("%d%d",&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!=-1;j=ne[j])
{
int k=e[j];
int a=id[i],b=id[k];
if(a!=b) dout[a]++; //记录出度数目
}
int sum=0,zero=0;
for(int i=1;i<=scc_cnt;i++)
if(!dout[i]) //找到出度为0的点。
{
zero++;
sum+=cnt[i];
if(zero>1)
{
sum=0;
break;
}
}
printf("%d\n",sum);
return 0;
}