
HMF-have much fun Site
文章
标签
16

阅

【题解】CF1140F Extending Set of Points
热度: loading...
线段树分治好题。
首先我们转换一下题意:将点 看做是连接第 行和第 列的一条边。题目所说的拓展集合大小即为每一个连通块中行和列能形成交点数。设连通块中有 个行点, 个列点,则该连通块对答案的贡献为
考虑修改操作,一个点会在某个时候出现,某个时候消失。也就是说,每个点在时间轴上是以一些区间的形式存在的。我们可以尝试用一个并查集来维护连通块以及其贡献,并把修改离线到时间轴上。
但我们发现并查集并不是一种利于删除的操作,它只支持回退最新的操作。如果我们要直接离线查询,那要删除并查集里一条边的时候这条边都不知道跑到哪里去了。所以我们需要一种方法来避免删除操作,只留下添加和撤回操作。
这里我们可以使用 线段树分治。在时间轴上建一棵线段树,把每一个点的存在区间下放到线段树上。然后我们从区间 开始二分,每进入一个区间时处理挂在这个区间上的修改操作。这样当我们到达 这个时间点时,所有覆盖在 这个时间上的修改操作都已经处理完了。之后在退出一个区间的时候将修改全部撤回即可。
// 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;
}
赏
