这种图论问题都挺考验小思维的.
首先,我们把从 $x$ 连出去两条边的都合并了.
然后再去合并从 $x$ 连出去一条原有边与一条新边的情况.
第一种情况直接枚举就行,第二种情况来一个多源 bfs 即可.
code:
#include <cstdio> #include <string> #include <vector> #include <queue> #include <algorithm> #define N 100006 #define ll long long using namespace std; namespace IO { inline void setIO(string s) { string in=s+".in"; string out=s+".out"; freopen(in.c_str(),"r",stdin); // freopen(out.c_str(),"w",stdout); } }; int edges; queue<int>q; vector<int>G[N]; int size[N],p[N],out[N],vis[N]; inline void add(int u,int v) { G[u].push_back(v); } inline int find(int x) { return p[x]==x?x:p[x]=find(p[x]); } inline void initialize() { for(int i=0;i<N;++i) size[i]=1,p[i]=i; } int main() { // IO::setIO("input"); int i,j,n,m; initialize(); scanf("%d%d",&n,&m); for(i=1;i<=m;++i) { int x,y; scanf("%d%d",&x,&y),++out[x],add(x,y); } for(i=1;i<=n;++i) { for(j=1;j<G[i].size();++j) { int pr=G[i][j-1],cur=G[i][j]; if(find(pr)!=find(cur)) { pr=find(pr),cur=find(cur); p[pr]=cur,size[cur]+=size[pr]; } } } for(i=1;i<=n;++i) { int x=find(i); if(size[x]>1) q.push(i),vis[i]=1; } while(!q.empty()) { int u=q.front(); q.pop(); for(i=0;i<G[u].size();++i) { int v=G[u][i]; if(find(v)!=find(u)) { int a=find(u),b=find(v); p[a]=b,size[b]+=size[a]; } if(!vis[v]) q.push(v),vis[v]=1; } } ll ans=0; for(i=1;i<=n;++i) { if(p[i]==i) ans+=(size[i]>1?(ll)(size[i]-1)*size[i]:out[i]); } printf("%lld\n",ans); return 0; }