#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
}
}