- 把每个点指向的点合并成一个强连通分量并且他们任意两点有边,例如存在边<a,b>,<a,c>,<a,d>,那么b、c、d合并成一个强连通分量
- 如果一个强连通分量里点的个数大于两个,那么这个强连通分量和他们的出边指向的点合并成一个强连通分量,例如存在边<a,b>,<a,c>,<a,d>,<d,e>,那么b、c、d、e合并成一个强连通分量
- 最后统计每个强连通里面的边数和强连通与强连通之间的边数
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn=200010;
int head[maxn],nume=1;
int n,m;
ll ans;
struct Edge
{
int v;
int next;
}e[maxn];
int fa[maxn],num[maxn];
void add_Edge(int u,int v)
{
e[nume].v=v;
e[nume].next=head[u];
head[u]=nume++;
}
int Find(int x)
{
return fa[x]==-1?x:fa[x]=Find(fa[x]);
}
void dfs(int u,int x)
{
for(int i=head[u];~i;i=e[i].next)
{
int y=e[i].v;
x=Find(x);
y=Find(y);
if(x!=y)
{
fa[y]=x;
num[x]+=num[y];
dfs(y,x);
}
}
}
int main()
{
cin>>n>>m;
for(int i=0;i<=n;i++)
{
num[i]=1;
fa[i]=head[i]=-1;
}
int u,v;
for(int i=1;i<=m;i++)
{
cin>>u>>v;
add_Edge(u,v);
}
bool update=true;
while(update)
{
update=false;
for(int j=1;j<=n;j++)
{
int x=num[Find(j)]==1?-1:j;
for(int i=head[j];~i;i=e[i].next)
{
int y=e[i].v;
if(x==-1) x=y;
else
{
x=Find(x);
y=Find(y);
if(x!=y)
{
fa[y]=x;
num[x]+=num[y];
update=true;
dfs(y,x);
}
}
}
}
}
for(int j=1;j<=n;j++)
{
if (fa[j]==-1) ans+=(ll)num[j]*(num[j]-1);
for (int i=head[j];~i;i=e[i].next) {
int x=Find(j),y=Find(e[i].v);
if (x!=y) ans++;
}
}
printf("%lld",ans);
return 0;
}