题意:给定一个有向图,问有多少个点由任意顶点出发都能达到。
分析:首先,在一个有向无环图中,能被所有点达到点,出度一定是0。
先求出所有的强连通分支,然后把每个强连通分支收缩成一个点,重新建图,这样,这个有向图就变成了一个有向无环图。
在这个新的图中,只需知道出度为0的点有几个即可。
如果出度为0的点超过1个,则输出0;否则输出出度为0的点所代表的那个强连通分支的分量数即可。
用Tarjan求强连通分量
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <string>
#include <vector>
#include <stack>
using namespace std;
#define N 10007 vector<int> G[N],G2[N];
stack<int> stk;
int instk[N],cnt,Time,n,m,out[N];
int low[N],dfn[N],bel[N],num[N]; void tarjan(int u)
{
low[u] = dfn[u] = ++Time;
stk.push(u);
instk[u] = ;
for(int i=;i<G[u].size();i++)
{
int v = G[u][i];
if(!dfn[v])
{
tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(instk[v])
low[u] = min(low[u],dfn[v]);
}
if(low[u] == dfn[u])
{
cnt++;
int v;
do
{
v = stk.top();
stk.pop();
instk[v] = ;
bel[v] = cnt;
num[cnt]++;
}while(u != v);
}
} void Tarjan()
{
memset(bel,,sizeof(bel));
memset(low,,sizeof(low));
memset(dfn,,sizeof(dfn));
memset(instk,,sizeof(instk));
memset(num,,sizeof(num));
memset(out,,sizeof(out));
while(!stk.empty())
stk.pop();
Time = cnt = ;
for(int i=;i<=n;i++)
if(!dfn[i])
tarjan(i);
} void Build()
{
int i,j;
for(i=;i<=cnt;i++)
G2[i].clear();
for(i=;i<=n;i++)
{
for(j=;j<G[i].size();j++)
{
int v = G[i][j];
if(bel[i] != bel[v])
{
G2[bel[i]].push_back(bel[v]);
out[bel[i]]++;
}
}
}
} int main()
{
int i,j,u,v;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(i=;i<=n;i++)
G[i].clear();
for(i=;i<m;i++)
{
scanf("%d%d",&u,&v);
G[u].push_back(v);
}
Tarjan();
Build();
int ans = ;
int flag = ;
int tag = ;
for(i=;i<=cnt;i++)
{
if(out[i])
continue;
if(flag)
{
tag = ;
break;
}
else
{
ans += num[i];
flag = ;
}
}
if(!tag)
puts("");
else
printf("%d\n",ans);
}
return ;
}