Link
不难发现扩展完之后,所有的点构成了若干个不相交的表格状的图形。
对于每条边,它有一些出现的时间区间。
因此我们线段树分治,利用并查集维护答案。
注意到需要撤销,因此必须使用不带路径压缩的按秩合并。
#include<map>
#include<stack>
#include<cstdio>
#include<cctype>
#include<vector>
#include<numeric>
#include<utility>
#include<algorithm>
#define fi first
#define se second
using i64=long long;
using pi=std::pair<int,int>;
int read(){int x=0,c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
const int N=600007;
std::map<pi,int>mp;std::vector<pi>e[N<<2];
int m,fa[N],sizex[N],sizey[N];i64 ans;
int find(int x){return x==fa[x]? x:find(fa[x]);}
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)>>1)
void update(int p,int l,int r,int L,int R,pi x)
{
if(l>R||L>r) return ;
if(L<=l&&r<=R) return e[p].push_back(x);
update(ls,l,mid,L,R,x),update(rs,mid+1,r,L,R,x);
}
void merge(int x,int y,std::stack<int>&stk)
{
if((x=find(x))==(y=find(y))) return ;
if(sizex[x]+sizey[x]<sizex[y]+sizey[y]) std::swap(x,y);
ans-=1ll*sizex[x]*sizey[x]+1ll*sizex[y]*sizey[y],fa[y]=x,sizex[x]+=sizex[y],sizey[x]+=sizey[y],ans+=1ll*sizex[x]*sizey[x],stk.push(y);
}
void solve(int p,int l ,int r)
{
i64 temp=ans;std::stack<int>stk;
for(auto x:e[p]) merge(x.fi,x.se+300000,stk);
if(l==r) printf("%lld ",ans); else solve(ls,l,mid),solve(rs,mid+1,r);
for(int x,y;!stk.empty();) x=stk.top(),stk.pop(),y=find(x),sizex[y]-=sizex[x],sizey[y]-=sizey[x],fa[x]=x;
ans=temp;
}
#undef ls
#undef rs
#undef mid
int main()
{
m=read();
for(int i=1,x,y;i<=m;++i) if(mp.find({x=read(),y=read()})==mp.end()) mp[{x,y}]=i; else update(1,1,m,mp[{x,y}],i-1,{x,y}),mp.erase(mp.find({x,y}));
for(auto t:mp) update(1,1,m,t.se,m,t.fi);
std::iota(fa+1,fa+600001,1),std::fill(sizex+1,sizex+300001,1),std::fill(sizey+300001,sizey+600001,1),solve(1,1,m);
}