https://www.luogu.com.cn/problem/P2863

#include<bits/stdc++.h>
#define lc p<<1
#define rc p<<1|1
#define INF 1e18
using namespace std;
#define lowbit(x) x&(-x)
#define endl '\n'
using ll = long long;
using int128 = __int128;
using pii = pair<ll,ll>;
const double PI = acos(-1);
const int N=1e4+10;
vector<int> e[N];
int dfn[N],low[N],idex;//dfn时间戳:第一次访问的结点,low:节点i能够回溯到的最早位于栈中的节点。(子树的根,可以理解为并查集的“祖先”一类的东西)
int scc[N],siz[N],cnt;//scc:每个点属于第几个强联通分量,siz:每个强联通分量有多少个点,cnt:强联通分量的个数
int stk[N],top;//stk:模拟栈
bool instk[N],vis[N];//instk:是否入栈,vis:该点是否被访问过
void tarjan(int x){
	dfn[x]=low[x]=++idex;
	stk[++top]=x;
	instk[x]=vis[x]=1;
	for(auto y:e[x]){
		if(!dfn[y]){ 
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(instk[y]){
			low[x]=min(low[x],dfn[y]);
		}
	}
	if(dfn[x]==low[x]){
		cnt++;
		int y=0;
		do{
			y=stk[top--];
			instk[y]=0;
			scc[y]=cnt;
			siz[cnt]++;
		}while(y!=x);
	}
}
void solve(){
	int n,m;cin>>n>>m;
	while(m--){
		int u,v;cin>>u>>v;
		e[u].push_back(v);
	}
	for(int i=1;i<=n;i++){
		if(!vis[i]) tarjan(i);
	}
	int ans=0;
	for(int i=1;i<=cnt;i++){
		if(siz[i]>1) ans++; 
	}
	cout<<ans;
}


int main() {
//	ios::sync_with_stdio(false);
//	cin.tie(nullptr), cout.tie(nullptr);
	
	int T = 1;
//	cin>>T;
	while (T--) {
		solve();
	}
	
	return 0;
}