题解:
结论很显然,双向连接的连通块会连成完全图,单向的会从前面向后连完整个连通块。
维护一下那些连通块之间有边,哪些点连向了自己,合并启发式合并即可,注意连环合并的情况。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
namespace IO{
inline char gc(){
static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}template<typename T>T get_integer(){
char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
}inline int gi(){return get_integer<int>();}
}using namespace IO;
using std::cerr;
using std::cout;
cs int N=1e5+7;
int n,m;
ll ans;
int fa[N],sz[N];
inline int gf(int x){
while(x!=fa[x])x=fa[x]=fa[fa[x]];
return x;
}
std::set<int> to[N],fr[N];
std::set<int> fr_nd[N];
std::vector<int> nd[N];
void merge_nd(int u,int v){
for(int w:nd[v]){
nd[u].push_back(w);
fr_nd[u].erase(w);
}nd[v].clear();
for(int w:fr_nd[v])
if(gf(w)!=u)fr_nd[u].insert(w);
fr_nd[v].clear();sz[u]+=sz[v];
}
void find_cap(cs std::set<int> &A,cs std::set<int> &B,std::queue<int> &q){
for(int u:A)if(B.find(u)!=B.end())q.push(u);
}
void merge_bl(int u,int v){
for(int a:fr[v])if(a!=u){
to[a].erase(v);
to[a].insert(u);
fr[u].insert(a);
}
for(int a:to[v])if(a!=u){
fr[a].erase(v);
fr[a].insert(u);
to[u].insert(a);
}fr[u].erase(v);to[u].erase(v);
fr[v].clear(),to[v].clear();
}
void merge_block(int a,int b){
std::queue<int> q;q.push(a),q.push(b);
while(!q.empty()){
int u=q.front();q.pop();
if(q.empty())break;
int v=q.front();q.pop();
u=gf(u),v=gf(v);
if(u==v){q.push(u);continue;}
ans-=(sz[u]-1ll)*sz[u]+(sz[v]-1ll)*sz[v];
if(sz[u]<sz[v])std::swap(u,v);
ans-=sz[u]*(ll)fr_nd[u].size();
ans-=sz[v]*(ll)fr_nd[v].size();
fa[gf(v)]=gf(u);merge_nd(u,v);
find_cap(fr[v],to[u],q);
find_cap(to[v],fr[u],q);
merge_bl(u,v);q.push(u);
ans+=(sz[u]-1ll)*sz[u];
ans+=sz[u]*(ll)fr_nd[u].size();
}
}
void add_edge(int u,int A){
if(fr_nd[A].find(u)==fr_nd[A].end()){
fr_nd[A].insert(u),ans+=sz[A];
fr[A].insert(gf(u));
to[gf(u)].insert(A);
}
}
void Main(){
n=gi();m=gi();
for(int i=1;i<=n;++i){
fa[i]=i,sz[i]=1;
nd[i].push_back(i);
}
while(m--){
int a=gi(),b=gi();
int A=gf(a),B=gf(b);
if(A==B){
cout<<ans<<"\n";
continue;
}
if(to[B].find(A)!=to[B].end())
merge_block(A,B);
else add_edge(a,B);
cout<<ans<<"\n";
}
}
inline void file(){
#ifdef zxyoi
freopen("joitter.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}