# Equivalent Sets

Time Limit: 12000/4000 MS (Java/Others)    Memory Limit: 104857/104857 K (Java/Others)
Total Submission(s): 5049    Accepted Submission(s): 1837

Problem Description
To prove two sets A and B are equivalent, we can first prove A is a subset of B, and then prove B is a subset of A, so finally we got that these two sets are equivalent.
You are to prove N sets are equivalent, using the method above: in each step you can prove a set X is a subset of another set Y, and there are also some sets that are already proven to be subsets of some other sets.
Now you want to know the minimum steps needed to get the problem proved.

Input
The input file contains multiple test cases, in each case, the first line contains two integers N <= 20000 and M <= 50000.
Next M lines, each line contains two integers X, Y, means set X in a subset of set Y.

Output
For each case, output a single integer: the minimum steps needed.

Sample Input
4 0 3 2 1 2 1 3

Sample Output
4 2
Hint
Case 2: First prove set 2 is a subset of set 1 and then prove set 3 is a subset of set 1.

Source

Recommend
xubiao

（要使这些点点构成强连通图，那他就必须有一个出度和入度）

/*求出强分量后缩点处理得到分支图，对分支图的每个

/*
dfn[i]：深度优先搜索遍历时节点i被搜索的次序。
low[i]：节点i能够回溯到的最早位于栈中的节点。
visit[i]：标记几点i是否在栈中。
number:是强连通分支的个数
*/
#include<iostream>
#include<cmath>
#include<cstring>
#include<stack>
#include<cstdio>
#include<string>
#include<vector>
#include<algorithm>
#define M 20010
using namespace std;
vector<int> G[M];
stack<int>q;
int dfn[M], low[M], in[M], out[M], belong[M];
bool visit[M];
int n, m, u, v, index, number;
void tarjan(int c)
{
dfn[c] = low[c] = ++index;
visit[c] = true;
q.push(c);
for (int i = 0; i < G[c].size(); i++)
{
int k = G[c][i];
if (!dfn[k])
{
tarjan(k);
low[c] = min(low[c], low[k]);//更新能找到的祖先
}
else if (visit[k])//这个条件很重要，这样可以避免已经确定在其他连通图的k,因为c到k的单向边而影响到c的low值
{
low[c] = min(low[c], dfn[k]);//能回到k被搜索时的次序
}
}
if (dfn[c] == low[c])
{
int p;
number++;//强连通分支的个数
do {
p = q.top();
q.pop();
belong[p] = number;
visit[p] = false;
} while (p != c);
}
}
void solve()
{
number = index = 0;
memset(dfn, 0, sizeof(dfn));
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
memset(visit, false, sizeof(visit));
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i);
if (number == 1)//特判，本身就是一个强连通图了
{
printf("0\n");
return;
}
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < G[i].size(); j++)
{
int kk = G[i][j];
if (belong[i] != belong[kk])//计算有几个出入度
{
out[belong[i]]++;
in[belong[kk]]++;
}
}
}
int max1 = 0, max2 = 0;
for (int i = 1; i <= number; i++)//计算出入度为0的个数
{
if (in[i] == 0)
max1++;
if (out[i] == 0)
max2++;
}
printf("%d\n", max(max1, max2));
}
int main()
{
while (scanf("%d%d", &n, &m) != EOF)
{
while (m--)
{
scanf("%d%d", &u, &v);
G[u].push_back(v);
}
solve();
for (int i = 1; i <= n; i++)
G[i].clear();
}
return 0;
}