// Slower C++ solution:
#include <bits/stdc++.h>

#include <bits/stdc++.h>

#include <ext/pb_ds/assoc_container.hpp>

#include <ext/pb_ds/tree_policy.hpp>

 

using namespace std;

using namespace __gnu_pbds;

 

#define fi first

#define se second

#define mp make_pair

#define pb push_back

#define fbo find_by_order

#define ook order_of_key

 

typedef long long ll;

typedef pair<ll,ll> ii;

typedef vector<int> vi;

typedef long double ld; 

typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> pbds;

typedef set<int>::iterator sit;

typedef map<int,int>::iterator mit;

typedef vector<int>::iterator vit;

 

vi adj[100001];

int out[100001];

int in[100001];

vi type[100001];

int color[100001];

bool used2 = false;

int cnt = 0;

int c[3];

ll mm = 0;

ll tote = 0;

bool dfs(int u, int col)

{

	used2|=(in[u]&&out[u]);

	cnt++; c[col]++;

	color[u] = col;

	bool res = 1;

	mm += out[u];

	tote++;

	for(int i = 0; i < adj[u].size(); i++)

	{

		int v = adj[u][i];

		int nw = (col+type[u][i])%3;

		if(color[v] == -1)

		{

			res&=dfs(v, nw);

		}

		else

		{

			if(color[v] != nw) res=false;

		}

	}

	return res;

}

 

int main()

{

	ios_base::sync_with_stdio(0); cin.tie(0);

	int n, m; cin>>n>>m;

	for(int i = 0; i < m; i++)

	{

		int u, v; cin>>u>>v; u--; v--;

		adj[u].pb(v); adj[v].pb(u);

		type[u].pb(1); type[v].pb(2);

		out[u]++; in[v]++;

	}

	memset(color,-1,sizeof(color));

	ll res = 0;

	for(int i = 0; i < n; i++)

	{

		if(color[i] == -1)

		{

			used2 = false; cnt = 0;

			memset(c,0,sizeof(c));

			mm = 0; tote = 0;

			bool ans=dfs(i, 0);

			//cerr<<c[0]<<' '<<c[1]<<' '<<c[2]<<' '<<mm<<'\n';

			if(ans)

			{

				if(used2)

				{

					for(int j = 0; j < 3; j++)

					{

						res += ll(c[j])*ll(c[(j+1)%3]);

					}

				}

				else

				{

					res += mm;

				}

			}

			else

			{	

				res += ll(tote)*ll(tote);

			}

		}

	}

	cout<<res<<'\n';

}


// Faster C++ solution for the same problem (measured speedup: 6.898835011893031x):
#include<bits/stdc++.h>

clock_t t=clock();

namespace IO

{

	using namespace std;

	char buf[1<<22],Out[1<<22],*p1=buf,*p2=buf;

	int p3=-1,f=0;

	inline void file()

	{

		#ifdef NTFOrz

		freopen("file.in","r",stdin);

		#endif

	}

	inline char getchar()

	{

		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;

	}

	inline void flush(){fwrite(Out,1,p3+1,stdout),p3=-1;}

	inline void check(){if (p3>(1<<21)) flush();}

	inline void putchar(char a){Out[++p3]=a;check();}

	inline void putline(string s)

	{

		int tmp=p3;

		while (p3-tmp<s.size()) putchar(s[p3-tmp]);

		putchar('\n');

	}

	inline char gc()

	{

		char c=getchar();

		while (!c||c=='\n'||c==' ') c=getchar();

		return c;

	}

	inline void chktime()

	{

		#ifdef NTFOrz

		printf("%dms\n",clock()-t);

		#endif

	}

}

namespace my_std

{

	using namespace std;

	#define getchar IO::getchar

	#define putchar IO::putchar

    #define Re register

    #define rep(i,a,b) for (Re int i=(a);i<=(b);i++)

    #define drep(i,a,b) for (Re int i=(a);i>=(b);i--)

    #define repv(i,v) for (Re int i=0;i<v.size();i++)

    #define go(u) for (Re int i=head[(u)];i;i=e[i].nxt)

    #define writesp(x) write(x),putchar(' '),IO::check()

    #define writeln(x) write(x),putchar('\n'),IO::check()

    #define mem(x,v) memset(x,v,sizeof(x))

    #define pb push_back

    #define fir first

    #define sec second

    #define MP make_pair

    typedef long long ll;

    typedef unsigned long long ull;

    typedef pair<int,int> pii;

    const int INF=0x7fffffff;

    inline int read()

    {

        ll sum=0,f=0;char c=getchar();

        while (!isdigit(c)) f|=(c=='-'),c=getchar();

        while (isdigit(c)) sum=(sum<<1)+(sum<<3)+(c^48),c=getchar();

        return f?-sum:sum;

    }

    void write(int k)

    {

        if (k<0) putchar('-'),k=-k;

        if (k>=10) write(k/10);

        putchar(k%10+48);

    }

	#define templ template<typename T>

    templ inline void chkmax(T &x,T y){if (x<y) x=y;}

    templ inline void chkmin(T &x,T y){if (x>y) x=y;}

}

using namespace my_std;

const int N=100010;

int n,m,ecnt,head[N],siz,flag,path,cnt[5],col[N];

ll ans;

struct edge{int to,nxt,dis;}e[N<<1];

inline void add_edge(int u,int v,int w){e[++ecnt].to=v,e[ecnt].nxt=head[u],e[ecnt].dis=w,head[u]=ecnt;}

inline int add(int x,int y){return x+y>=3?x+y-3:x+y;}

void dfs(int u)

{

	siz++,cnt[col[u]]++;

	go(u)

	{

		int v=e[i].to;

		path+=(e[i].dis==1);

		if (col[v]==INF)

		{

			col[v]=add(col[u],e[i].dis);

			dfs(v);

		}

		else if (col[v]!=add(col[u],e[i].dis)) flag=1;

	}

}

int main()

{

	IO::file();

	n=read(),m=read();

	rep(i,1,n) col[i]=INF;

	rep(i,1,m)

	{

		int u=read(),v=read();

		add_edge(u,v,1),add_edge(v,u,2);

	}

	rep(i,1,n) if (col[i]==INF)

	{

		siz=flag=path=col[i]=0;

		memset(cnt,0,sizeof(cnt));

		dfs(i);

		if (!flag)

		{

			if (cnt[0]&&cnt[1]&&cnt[2]) ans+=1ll*cnt[0]*cnt[1]+1ll*cnt[1]*cnt[2]+1ll*cnt[2]*cnt[0];

			else ans+=path;

		}

		else ans+=1ll*siz*siz;

	}

	cout<<ans<<endl;

	IO::flush();

	IO::chktime();

	return 0;

}