在有向图中,如果两个顶点间至少存在一条路径,称两个顶点强连通。
如果有向图G的每两个顶点都强连通,称G是一个强连通图。
非强连通图有向图的极大强连通子图,称为强连通分量。
而用tarjan算法可以起求出各个强连通分量,然后再把强连通分量缩成一个点,非强连通图就被转换成一个
DAG,去多问题都是在此基础上求解。
以下是模板:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;
#define maxn 10010
vector <int> G[maxn];
int pre[maxn], lowlink[maxn], sccno[maxn], dfs_clock, scc_cnt;
//scc_cnt为SCC的计数器,sccno[i]为i所在SCC的编号
stack <int> S;
int out0[maxn];
void dfs(int u)
{
pre[u]=lowlink[u]=++dfs_clock;
S.push(u);
for(int i = 0; i < G[u].size(); i++)
{
int v=G[u][i];
if(!pre[v]) //如果没有访问则访问
{
dfs(v);
lowlink[u]=min(lowlink[u],lowlink[v]);
}else if(!sccno[v]) //如果访问了且在栈中
lowlink[u]=min(lowlink[u],pre[v]);
}
if(lowlink[u]==pre[u])
{
scc_cnt++;
while(1)
{
int x=S.top(); S.pop();
sccno[x]=scc_cnt;
if(x==u)break;
}
}
}
void find_scc(int n)
{
dfs_clock=scc_cnt=0;
memset(sccno,0,sizeof(sccno));
memset(pre,0,sizeof(pre));
for(int i = 1; i <= n; i++)if(!pre[i])dfs(i);//节点编号从1开始
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
for(int i = 0; i <= n; i++)G[i].clear();
for(int i = 0; i < m; i++)
{
int a,b;
scanf("%d%d",&a,&b);
G[a].push_back(b);
}
find_scc(n);
if(scc_cnt==1)
{
printf("%d\n",n);
continue;
}
memset(out0,0,sizeof(out0));
for(int u = 1; u <= n; u++)
for(int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
if(sccno[u]!=sccno[v])
out0[sccno[u]]++;
}
int a=0,id;
for(int i = 1; i <= scc_cnt; i++)
if(out0[i]==0){a++;id=i;}
if(a==1)
{
int ans = 0;
for(int i = 1; i <= n; i++)
if(sccno[i]==id)ans++;
printf("%d\n",ans);
}
else
{
printf("0\n");
}
}
return 0;
}