CF1140F解题报告

这是一道我自己认为线段树分治场切的一道好题。适合以后来复习。

题目大意:
一个二维集合 \(S\),令 \(R=S\),如果 \((x_1,y_1)\in R,(x_1,y_2)\in R,(x_2,y_1)\in R\) 那么 \((x_2,y_2)\) 也一定在 \(R\) 里面。动态修改 \(S\),求修改后的 \(|R|\)

这道题,我们可以先分析 \((x_1,y_1)\in R,(x_1,y_2)\in R,(x_2,y_1)\in R\) 有什么性质。

因为第二维与第一维的元素是独立的,所以我们试着把它分为两行。

一个二元组就是上面一行向下一行连边。

考虑一个上面和下面连接的元素数量,如果上面有两个不一样的元素都向下面同一个元素连了边,就是满足了 \((x_1,y_1)\in R,(x_2,y_1)\in R\)。只需要找到上面连边的其他元素就能连边。设一个连通块上面的数量为 \(a\) 个,下面有 \(b\) 个,则总体的边数应该是 \(ab\) 条边。

我们设计一个并查集,记录一下 \(sza\)\(szb\)\(sz\)\(fath\) 就行。每一次连边就是把所有 \(sz\) 相加。使用可撤销并查集。

考虑怎么记录答案。直接将原来的两个连通块的答案删除,再加上新的答案即可。

对于后面的撤销操作,直接将答案还原成为还没有更改时的答案即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
namespace gtx{
//	Fast IO
	void read(int &x){
		x = 0;int h = 1;char tmp;
		do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
		while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
		x*=h;
	}
	void read(char &x){do{x=getchar();}while(x==' '||x=='\n'||x=='\r');}
	void write(char x){putchar(x);}
	void write(int x){
		if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
		do st[++tot]=x%10,x/=10; while(x);
		while(tot){putchar(st[tot--]+'0');}
	}
	void write(int x,char y){write(x);write(y);}
#ifndef int
	void read(long long &x){
		x = 0;int h = 1;char tmp;
		do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
		while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
		x*=h;
	}
	void write(long long x){
		if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
		do st[++tot]=x%10,x/=10; while(x);
		while(tot){putchar(st[tot--]+'0');}
	}
	void write(long long x,char y){write(x);write(y);}
#endif
	const int MAXN = 6e5+10;
	struct segmentree{
		int l,r;
		vector<pair<int,int>> v;
	}tree[MAXN<<2];
	void build(int k,int l,int r){
		tree[k].l = l;
		tree[k].r = r;
		if(l==r) return;
		int mid = (l+r)>>1;
		build(k*2,l,mid);
		build(k*2+1,mid+1,r);
	}
	void modify(int k,int l,int r,int x,int y){
		if(tree[k].l>r||tree[k].r<l) return;
		if(tree[k].l>=l&&tree[k].r<=r){
			tree[k].v.push_back({x,y});
			return;
		}
		modify(k*2,l,r,x,y);
		modify(k*2+1,l,r,x,y);
	}
	int fath[MAXN];
	int sza[MAXN],szb[MAXN],sz[MAXN];
	int get_father(int k){
		if(k==fath[k]) return k;
		return get_father(fath[k]);
	}
	struct ids{
		int id;
		int sza;
		int szb;
		int sz;
	};
	stack<pair<pair<int,ids>,pair<int,ids>>> st;
	int nowans;
	void merge(int x,int y){
		int fx = get_father(x);
		int fy = get_father(y);
		if(fx==fy) return st.push({{fx,{fx,sza[fx],szb[fx],sz[fx]}},
					        {fy,{fy,sza[fy],szb[fy],sz[fy]}}}),void();
		if(sz[fx]>sz[fy]) swap(fx,fy);
		nowans -= sza[fx]*szb[fx];
		nowans -= sza[fy]*szb[fy];
		st.push({{fx,{fx,sza[fx],szb[fx],sz[fx]}},
				 {fy,{fy,sza[fy],szb[fy],sz[fy]}}});
		fath[fx] = fy;
		sz[fy] += sz[fx];
		sza[fy] += sza[fx];
		szb[fy] += szb[fx];
		nowans += sza[fy]*szb[fy];
	}
	void CX(){
		auto tmp = st.top();st.pop();
		fath[tmp.first.first] = tmp.first.first;
		sz[tmp.first.first] = tmp.first.second.sz;
		sza[tmp.first.first] = tmp.first.second.sza;
		szb[tmp.first.first] = tmp.first.second.szb;
		fath[tmp.second.first] = tmp.second.first;
		sz[tmp.second.first] = tmp.second.second.sz;
		sza[tmp.second.first] = tmp.second.second.sza;
		szb[tmp.second.first] = tmp.second.second.szb;
	}
	int ans[MAXN];
	void solve(int k){
		int tot = 0;
		int rightans = nowans;
		for(auto i:tree[k].v){
			merge(i.first,i.second+3e5);
			tot++;
		}
		if(tree[k].l==tree[k].r){
			ans[tree[k].l] = nowans;
			nowans = rightans;
			while(tot--) CX();
			return;
		}
		solve(k*2);
		solve(k*2+1);
		nowans = rightans;
		while(tot--) CX();
	}
	int n;
	map<pair<int,int>,int> mp;
	signed main(){
		read(n);
		fill(sza+1,sza+1+300000,1);
		fill(szb+1+300000,szb+600000+1,1);
		fill(sz+1,sz+1+600000,1);
		iota(fath+1,fath+1+600000,1);
		build(1,1,n+1);
		for(int i = 1;i<=n;i++){
			int x,y;
			read(x);read(y);
			if(mp.find({x,y})==mp.end()){
				mp[{x,y}] = i;
			}else{
				modify(1,mp[{x,y}],i-1,x,y);
				mp.erase({x,y});
			}
		}
		for(auto i:mp){
			modify(1,i.second,n+1,i.first.first,i.first.second);
		}
		solve(1);
		for(int i = 1;i<=n;i++) write(ans[i],' ');
		return 0;
	}
}
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
//	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int T = 1;
//	gtx::read(T);
	while(T--) gtx::main();
	return 0;
}
posted @ 2024-12-26 22:09  辜铜星  阅读(3)  评论(0编辑  收藏  举报