Accepted | 1708K | 625MS | 2318B |
/*
后来看了别人的代码后发现,其实可以找到强连通分量之后,
把其中的每个元素都染成一个颜色(开一个数组paint[]来记录染色情况,颜色用num表示,初始化为),
最后枚举所有边,如果一条边(u,v)两个端点的颜色不一样,那么就让outdgr[paint[u]]++。
即让这个强连通分支的出度加一
当然,这样最后outdgr中存的并不是强连通分量的出度,但如果outdgr的值为,那么该强连通分量的值也一定为,
因此用outdgr来判断强连通分量的出度是否为是完全可以的。
*/
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
#define Max 10005
bool visit[Max]; //标记该点是否已经完全访问过了,即是否已经发现过了
bool inS[Max]; //标记是否在栈中
vector<int> v[Max]; ///类似与邻接表,用于存储图
vector<int> conn[Max];//记录各个强连通分支
int dfn[Max]; //用于记录第一次访问时的时间
int low[Max]; //low及其后代所能访问追溯到的最早祖先的dfn的值
int out[Max]; //记录强连通分支的出度
int paint[Max]; //给强连通分支内的元素染色,如果一个边的两个端点颜色不一样,则强连通分支的出度加一
int time; //时间戳
int num; //连通分支数量
int N,M; //节点与操作数
stack<int> S;
void Tarjan(int u)
{
int next;
low[u] = dfn[u] = ++time;
S.push(u);
inS[u] = true;
visit[u] = true;
for(int i=0;i<v[u].size();i++)
{
next = v[u][i];
if(!visit[next])//如果u的下一个顶点还没有访问
{
Tarjan(next);
low[u] = min(low[u],low[next]);
}else{
if(inS[u])//已经访问过该点了,而且还在栈里
low[u] = min(low[u],dfn[next]);
}
}
if(dfn[u]==low[u])//如果dfn==low证明该点为深搜树的树根
{
do{
next = S.top();
paint[next] = num;
conn[num].push_back(next);//依次记录各个连通分支
S.pop();
inS[next] = false;
}while(next!=u);
num++;//计数
}
}
int main()
{
int i;
int a,b;
int count;
while(scanf("%d%d",&N,&M)!=EOF)
{
for(i=0;i<=N;i++)
{
conn[i].clear();
v[i].clear();//在下一次运行前清空邻接表
}
memset(visit,false,sizeof(visit));
memset(inS,false,sizeof(inS));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(out,0,sizeof(out));
memset(paint,-1,sizeof(paint));
time = 0;
num = 0;//初始化完毕
for(i=0;i<M;i++)
{
scanf("%d%d",&a,&b);
v[a].push_back(b);//将b加入a的邻接表中
}
for(i=1;i<=N;i++)
if(!visit[i])
Tarjan(i);//对元素遍历求出强连通分支和分支数
//遍历该图的所有边,找到颜色不一样的,改变该连通分支out的值
for(i=1;i<=N;i++)
{
if(v[i].empty())//如果没有该点
continue;
for(int j=0;j<v[i].size();j++)
{
if(paint[i]!=paint[v[i][j]])
out[paint[i]]++;
}
}
int res=0;
count = 0;
for(i=0;i<num;i++)
if(out[i]==0)
{
count++;
res = i;
}
if(count>1)
{
printf("0\n");
continue;
}
printf("%d\n",conn[res].size());
}
return 0;
}