#include <bits/stdc++.h>
using namespace std;
//设置最大节点数
const int maxv = 10005;
//邻接表中的节点结构体,next代表后继节点编号
struct node{
int next;
node(int _n):next(_n){}
};
//DFN数组记录每个节点入栈的时间戳,DFN另一个作用可以检测该节点是否已经访问过;
//LOW节点记录每个节点的最小根节点
int DFN[maxv],LOW[maxv];
//时间戳,相当于计时器,每搜索到一个节点index+1
int index = 1;
//栈容器,每搜索到一个节点就将他入栈,等找到一个强连通分量之后就将该强连通分量的所有节点出栈
int vstack[maxv];
//栈顶指针
int top = -1;
//记录节点是否在栈中
int flag[maxv];
//图的邻接表
vector<node> maze[maxv];
int n,m,ans;
//tarjan核心算法:
void tarjan(int u){
//每来访问一个几点,记录该节点的时间戳,并初始化它的最小根节点LOW
DFN[u]=LOW[u] = index++;
//节点入栈
vstack[++top]=u;
//置入栈标识为1
flag[u]=1;
//遍历当前节点的所有后继节点
for(int i=0;i<maze[u].size();i++){
//后继节点v
int v = maze[u][i].next;
//如果v没有被访问过,就递归进入tarjan(v),回来之后取最小的根节点给LOW[u]
//如果v被访问并且v在栈中,LOW[u]与DFN[v]的较小值给LOW[u]
if(!DFN[v]){
tarjan(v);
LOW[u] = min(LOW[u],LOW[v]);
}else if(flag[v]){
LOW[u] = min(LOW[u],DFN[v]);
}
}
//如果DFN[u]=LOW[u]说明已经找到了强连通分量,可以进行输出等相关操作
if(DFN[u]==LOW[u]){
//int num=0;
do{
//此处可以进行一系列相关操作,根据需要进行修改!
j = vstack[top--];//取出强连通分量里面的节点,出栈
//num++;
flag[j]=0;//并置此节点不在栈中
}while(j!=u);
//ans = ans+num*(num-1)/2;
}
}
int main(){
//初始化操作
fill(DFN,DFN+maxv,0);
fill(LOW,LOW+maxv,0);
fill(flag,flag+maxv,0);
scanf("%d %d",&n,&m);
for(int i=0;i<m;i++){
int x,y;
scanf("%d %d",&x,&y);
maze[x].push_back(node(y));
}
for(int i=0;i<n;i++){
if(!DFN[i])tarjan(i);
}
//printf("%d\n",ans);
return 0;
}