// hdu Equivalent Sets.cpp : 定义控制台应用程序的入口点。
//
/*
题目描述:将题目中的集合转换为顶点,A集合是B集合的子集,转换为一条有向边<A,B>,即题目给我们一个有向图,问最少需要添加多少条边使之成为强连通图。
解题思路:通过tarjan算法找出图中的所有强连通分支,并将每一个强连通分支缩成一个点(因为强连通分量本身已经满足两两互相可达)。
要使缩点后的图成为强连通图,每个顶点最少要有一个入度和一个出度,一条边又提供一个出度和一个入度。
所以可以通过统计没有入度的顶点数 noInDegree 和 没有出度的顶点数 noOutDegree。
所需要添加的边数就是noInDegree和noOutDegree中的最大值。
*/
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
const int MAXN = 20000+10;
vector<int>grap[MAXN]; //稀疏图,用邻接表表示图
stack<int>S;
int low[MAXN]; //low[u] 为u或u的子树能够追溯到的最早的栈中节点的次序编号
int num[MAXN]; //num[u] 为u搜索的次序编号(时间戳)
int visited[MAXN]; //标记是否已经被搜索过
int instack[MAXN]; //标记是否在栈中
int index; //顶点的前序编号
int cnt_scc; //记录总共将图缩成多少个点
int belong[MAXN]; //belong[i] = j 表示原图的点i缩点后为点j
int min(int a, int b)
{
return a < b ? a : b;
}
int max(int a, int b)
{
return a > b ? a : b;
}
//初始化
void init(int n)
{
for(int i=0; i<=n; i++)
{
grap[i].clear();
}
while(!S.empty())
{
S.pop();
}
memset(instack,0,sizeof(instack));
memset(visited,0,sizeof(visited));
memset(low,-1,sizeof(low));
memset(num,-1,sizeof(num));
memset(belong,-1,sizeof(belong));
index = 0;
cnt_scc = 0;
}
//找出连通分支,并缩点
void tarjan(int v)
{
low[v] = num[v] = ++index;
S.push(v);
instack[v] = 1;
visited[v] = 1;
for(int i=0; i<grap[v].size(); i++)
{
int w = grap[v][i];
if(!visited[w])
{
tarjan(w);
low[v] = min(low[v],low[w]); //v或v的子树能够追溯到的最早的栈中节点的次序编号
}
else if(instack[w]) //(v,w)为后向边
{
low[v] = min(low[v],num[w]);
}
}
int u;
if(low[v] == num[v]) //满足强连通分支条件,进行缩点
{
++cnt_scc;
do
{
u = S.top();
belong[u] = cnt_scc; //缩点
S.pop();
instack[u] = 0; //出栈解除标记
}while(u != v);
}
}
int main()
{
int n,m;
int a,b;
int indegree[MAXN];
int outdegree[MAXN];
while(cin>>n>>m)
{
init(n);
memset(indegree,0,sizeof(indegree));
memset(outdegree,0,sizeof(outdegree));
while(m--)
{
cin>>a>>b;
grap[a].push_back(b);
}
for(int i=1; i<=n; i++)
{
if(!visited[i])
{
tarjan(i);
}
}
//求缩点后,各个顶点的出度和入度
for(int i=1; i<=n; i++)
{
for(int j=0; j<grap[i].size(); j++)
{
int k = grap[i][j];
if(belong[i] != belong[k])
{
indegree[belong[k]]++;
outdegree[belong[i]]++;
}
}
}
a = b = 0;
for(int i=1; i<=cnt_scc; i++)
{
if(!indegree[i])
{
a++;
}
if(!outdegree[i])
{
b++;
}
}
if(cnt_scc == 1) //如果图已经为强连通图
{
cout<<"0"<<endl;
}
else
{
cout<<max(a,b)<<endl;
}
}
return 0;
}