#include<bits/stdc++.h>
using namespace std;
int n; // 节点数量
int G[1010][1010]; // 图的邻接矩阵表示
int Time,sum; // Time用于记录DFS的时间,sum用于记录强连通分量的数量
int beg[1010],low[1010]; // beg记录每个节点开始被访问的时间,low记录每个节点能够回溯到的最早的节点的时间
stack<int> S; // 用于DFS的栈
int in_stack[1010]; // 记录每个节点是否在栈中
void DFS(int v); // 深度优先搜索函数
int main(){
int a,b,m; // a和b用于读取边的两个节点,m用于读取边的数量
while(scanf("%d",&n)!=EOF){ // 读取节点数量
scanf("%d",&m); // 读取边的数量
memset(G,0,sizeof(G)); // 初始化图的邻接矩阵
for(int i=1;i<=m;i++){ // 读取所有的边
scanf("%d%d",&a,&b);
G[a][b]=1; // 在邻接矩阵中添加边
}
Time=sum=0; // 初始化时间和强连通分量数量
memset(beg,0,sizeof(beg)); // 初始化每个节点的开始访问时间
memset(low,0,sizeof(low)); // 初始化每个节点的最早可达节点的时间
memset(in_stack,0,sizeof(in_stack)); // 初始化每个节点是否在栈中的标记
for(int z=1;z<=n;z++){ // 对每个节点进行深度优先搜索
if(beg[z]==0){
DFS(z);
}
}
printf("%d\n",sum); // 输出强连通分量的数量
}
return 0;
}
void DFS(int v){ // 深度优先搜索函数
beg[v]=low[v]=++Time; // 初始化每个节点的开始访问时间和最早可达节点的时间
S.push(v); // 将节点放入栈中
in_stack[v]=1; // 标记节点在栈中
for(int i=1;i<=n;i++){ // 遍历所有节点
if(G[v][i]==1){ // 如果存在从v到i的边
if(beg[i]==0){ // 如果节点i还没有被访问过
DFS(i); // 对节点i进行深度优先搜索
low[v]=min(low[v],low[i]); // 更新节点v的最早可达节点的时间
}else if(in_stack[i]==1){ // 如果节点i在栈中
low[v]=min(low[v],low[i]); // 更新节点v的最早可达节点的时间
}
}
}
if(beg[v]==low[v]){ // 如果节点v是一个强连通分量的根节点
int t;
sum++; // 强连通分量数量加1
do{
t=S.top(); // 取出栈顶元素
S.pop(); // 弹出栈顶元素
in_stack[t]=0; // 标记节点t已经不在栈中
}while(t!=v); // 直到弹出的是节点v
}
}