题目链接:Codeforces - Mr. Kitayuta’s Technology
我们可以发现如果联通块存在环,若环的大小为n,那么最少必然需要n条边。
如果不存在环,我们对于每一个联通块最少必然需要n-1条边。
所以我们对每个联通块判环然后分别计数即可。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e5+10;
int n,m,scc[N],vis[N],num[N],low[N],dfn[N],cnt,res,co,mark[N],f[N];
vector<int> g[N]; stack<int> st;
void Tarjan(int x){
low[x]=dfn[x]=++cnt,vis[x]=1,st.push(x);
for(auto to:g[x]){
if(!dfn[to]){
Tarjan(to); low[x]=min(low[x],low[to]);
}else if(vis[to]) low[x]=min(low[x],dfn[to]);
}
if(low[x]==dfn[x]){
co++; int u;
do{
u=st.top(); vis[u]=0; scc[u]=co; num[co]++; st.pop();
if(num[co]>1) mark[co]=1;
}while(u!=x);
}
}
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
signed main(){
cin>>n>>m;
for(int i=1,a,b;i<=m;i++) cin>>a>>b,g[a].push_back(b);
for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
for(int i=1;i<=co;i++) f[i]=i;
for(int i=1;i<=n;i++)
for(auto to:g[i]){
int x=find(scc[i]),y=find(scc[to]);
if(x^y) f[x]=y,num[y]+=num[x],mark[y]|=mark[x];
}
for(int i=1;i<=co;i++) if(find(i)==i){
if(mark[i]) res+=num[i];
else res+=num[i]-1;
}
cout<<res;
return 0;
}