一、题目
二、解法
我们考虑把每个点拆成两个点,然后每个点对就是这个二分图里面的边。
考虑一个连通的二分图(用给出的点对连接)的所有边都会存在,这里给出归纳法的证明。一开始都是单个点,所以满足,如果加入一条 ( x 1 , y 1 ) (x_1,y_1) (x1,y1)的边,那么和 x 1 x_1 x1相连的 y y y,和 y 1 y_1 y1相连的 x x x都会连接,原来是一个完全二分图的话现在还会是一个完全二分图。
可以用启发式合并并查集来维护这个二分图,回退就记录一下就行了,维护一下 x x x区域的点和 y y y区域的点,贡献就是乘积,还有不懂的可以康康代码。
#include <cstdio>
#include <vector>
#include <map>
using namespace std;
const int M = 600005;
#define mk make_pair
#define ll long long
#define pii pair<int,int>
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,tp,fa[M],xc[M],yc[M],dep[M],st[2*M];ll ans;
map<pii,int> mp;vector<pii> v[2*M];
int find(int x)
{
return x==fa[x]?x:find(fa[x]);
}
void merge(int x,int y)
{
if(x==y) return ;
if(dep[x]<dep[y]) swap(x,y);
ans-=1ll*xc[y]*yc[y];
ans-=1ll*xc[x]*yc[x];
fa[y]=x;xc[x]+=xc[y];yc[x]+=yc[y];
ans+=1ll*xc[x]*yc[x];
st[++tp]=x;st[++tp]=y;
if(dep[x]==dep[y]) dep[x]++,st[tp]*=-1;
}
void back(int t)
{
while(tp>t)
{
int x=st[tp--],y=st[tp--];
ans-=1ll*xc[y]*yc[y];
if(x<0) dep[y]--,x=-x;
fa[x]=x;xc[y]-=xc[x];yc[y]-=yc[x];
ans+=1ll*xc[y]*yc[y];
ans+=1ll*xc[x]*yc[x];
}
}
void ins(int i,int l,int r,int L,int R,pii x)
{
if(L>r || l>R) return ;
if(L<=l && r<=R)
{
v[i].push_back(x);
return ;
}
int mid=(l+r)>>1;
ins(i<<1,l,mid,L,R,x);
ins(i<<1|1,mid+1,r,L,R,x);
}
void ask(int i,int l,int r)
{
int cur=tp;
for(int j=0;j<v[i].size();j++)
{
int x=v[i][j].first,y=v[i][j].second;
merge(find(x),find(y));
}
if(l==r)
{
printf("%lld ",ans);
back(cur);
return ;
}
int mid=(l+r)>>1;
ask(i<<1,l,mid);
ask(i<<1|1,mid+1,r);
back(cur);
}
int main()
{
n=3e5;m=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read()+n;pii t=mk(u,v);
if(mp.count(t))
ins(1,1,m,mp[t],i-1,t),mp.erase(t);
else mp[t]=i;
}
for(map<pii,int>::iterator it=mp.begin();it!=mp.end();it++)
ins(1,1,m,it->second,m,it->first);
for(int i=1;i<=n+n;i++)
{
fa[i]=i;
if(i<=n) xc[i]++;
else yc[i]++;
}
ask(1,1,m);
}