音乐播放器
HMF-have much fun Site
 
文章 标签
16

Powered by Gridea | Theme: Fog
载入天数...
载入时分秒...
总访问量:  |   访问人数:

【题解】CF1140F Extending Set of Points

  热度: loading...

线段树分治好题。

首先我们转换一下题意:将点 (x,y)(x,y) 看做是连接第 xx 行和第 yy 列的一条边。题目所说的拓展集合大小即为每一个连通块中行和列能形成交点数。设连通块中有 xnx_n 个行点,yny_n 个列点,则该连通块对答案的贡献为 xn×ynx_n \times y_n

考虑修改操作,一个点会在某个时候出现,某个时候消失。也就是说,每个点在时间轴上是以一些区间的形式存在的。我们可以尝试用一个并查集来维护连通块以及其贡献,并把修改离线到时间轴上。

但我们发现并查集并不是一种利于删除的操作,它只支持回退最新的操作。如果我们要直接离线查询,那要删除并查集里一条边的时候这条边都不知道跑到哪里去了。所以我们需要一种方法来避免删除操作,只留下添加和撤回操作。

这里我们可以使用 线段树分治。在时间轴上建一棵线段树,把每一个点的存在区间下放到线段树上。然后我们从区间 [1,n][1,n] 开始二分,每进入一个区间时处理挂在这个区间上的修改操作。这样当我们到达[i,i][i,i] 这个时间点时,所有覆盖在 ii 这个时间上的修改操作都已经处理完了。之后在退出一个区间的时候将修改全部撤回即可。

// Problem: CF1140F Extending Set of Points
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF1140F
// Memory Limit: 1000 MB
// Time Limit: 3500 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
#define ls k*2
#define rs k*2+1
#define mid (l+r)/2
#define mp make_pair
#define fi first
#define se second
#define pb push_back
#define int long long
#define N 600010
using namespace std;
typedef pair<int,int> pii;
int read() {
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) f=ch=='-'?-1:1,ch=getchar();
	while(isdigit(ch)) res=res*10+ch-'0',ch=getchar();
	return f*res;
}
int n,fa[N],size[N],tp;
pii siz[N];
int find(int x) {
	while(fa[x]!=x) x=fa[x];
	return x;
}
map<pii,int> pt;
vector<pii> p[N<<2];
int res;
struct node{
	int x,y;
	pii siz;
	int size,res;
}sk[N];
void merge(int x,int y) {
	x=find(x),y=find(y);
	if(x==y) return ;
	if(size[x]>size[y]) swap(x,y);
	sk[++tp]=node{x,y,siz[y],size[y],res};
	res-=1ll*siz[x].fi*siz[x].se+1ll*siz[y].fi*siz[y].se;
	fa[x]=y,size[y]+=size[x];
	siz[y]=mp(siz[y].fi+siz[x].fi,siz[y].se+siz[x].se);
	res+=1ll*siz[y].fi*siz[y].se;
}
void modify(int k,int l,int r,int L,int R,pii val) 
{
	if(L<=l&&r<=R) {p[k].pb(val);return ;}
	if(L<=mid) modify(ls,l,mid,L,R,val);
	if(R>mid) modify(rs,mid+1,r,L,R,val);
}
void solve(int k,int l,int r) {
	int las=tp;
	for(int i=0; i<p[k].size(); i++) 
		merge(p[k][i].fi,p[k][i].se);
	if(l==r) printf("%lld ",res);
	else solve(ls,l,mid),solve(rs,mid+1,r);
	while(tp>las) {
	    int x=sk[tp].x,y=sk[tp].y;
	    fa[x]=x,size[y]=sk[tp].size;
	    siz[y]=sk[tp].siz;
	    res=sk[tp].res;
	    tp--;
	}
}
signed main()
{
	n=read();
	for(int i=1; i<=n; i++) {
		pii tmp=mp(read(),read()+300000);
		if(pt[tmp]) modify(1,1,n,pt[tmp],i-1,tmp),pt.erase(tmp);
		else pt[tmp]=i;
	}
	for(int i=1; i<=600000; i++) 
	    fa[i]=i,siz[i]=((i<=300000)?mp(1,0):mp(0,1)),size[i]=1;
	for(auto i:pt) modify(1,1,n,i.se,n,i.fi);
	solve(1,1,n);
	return 0;
}