P4121 [WC2005]双面棋盘 线段树分治+可撤销并查集

题意:

戳这里

分析:

动态维护图的连通性:

常见的离线做法有两种 , LCT线段树分治

其中线段树分治常见的有两种:

  1. 操作之间独立
  2. 操作不独立,这种情况下大部分是按时间进行分治

对于这个题,我们要维护两种颜色的连通块个数,很容易想到并查集,但是并查集不支持删边/kk,那我们换个思路,我们把删边操作看成出现一段时间,这样只需要一个可撤销并查集

举个栗子:

\(x,y\) 这个点由 白色 变成 黑色,首先把它周围的白点的边和它断开,再把周围黑色点和它合并,维护连通块个数

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second
#define inl inline
#define reg register

using namespace std;

namespace zzc
{
	inline int read()
	{
		int x=0,f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	const int maxn = 1e5+5;
	int n,m,tot;
	int col[205][205],frm[maxn<<3],to[maxn<<3],lst[maxn<<3],cnt[maxn];
	int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
	map<pii,int> pid;
	struct info
	{
		int col,u,v;
		info(){}
		info(int col,int u,int v):col(col),u(u),v(v){}
	};
	vector<info> t[maxn<<2];
	
	struct dsu
	{
		int top,cnt;
		int fa[maxn],siz[maxn],st[maxn];
		
		void init(int num)
		{
			for(int i=1;i<=num;i++) fa[i]=i,siz[i]=1;
		}
		
		int find(int x)
		{
			return fa[x]==x?x:find(fa[x]);
		}
		
		void merge(int x,int y)
		{
			int fx=find(x),fy=find(y);
			if(fx!=fy)
			{
				if(siz[fx]<siz[fy]) swap(fx,fy);
				st[++top]=fy;cnt++;
				fa[fy]=fx;siz[fx]+=siz[fy];
			}
		}
		
		void del(int num)
		{
			while(top>num)
			{
				siz[fa[st[top]]]-=siz[st[top]];
				fa[st[top]]=st[top];
				top--;cnt--;
			}
		}
		
	}bla,whi;
	
	inline int id(int c,int x,int y)
	{
		return c*n*n+(x-1)*n+y;
	}
	
	void modify(int rt,int l,int r,int ql,int qr,info x)
	{
		if(ql<=l&&r<=qr)
		{
			t[rt].pb(x);
			return ;
		}
		int mid=(l+r)>>1;
		if(ql<=mid) modify(lc,l,mid,ql,qr,x);
		if(qr>mid) modify(rc,mid+1,r,ql,qr,x);
	}
	
	void add(int x,int y)
	{
		if(x>y) swap(x,y);
		pid[mk(x,y)]=++tot;
		frm[tot]=x;
		to[tot]=y;
	}
	
	void link(int x,int y,int tim)
	{
		if(x>y) swap(x,y);
		lst[pid[mk(x,y)]]=tim;
	}
	
	void cut(int c,int x,int y,int tim)
	{
		if(x>y) swap(x,y);
		modify(1,0,m,lst[pid[mk(x,y)]],tim-1,info(c,x,y));
		lst[pid[mk(x,y)]]=-1;
	}
	
	void solve(int rt,int l,int r)
	{
		int top1=bla.top,top2=whi.top;
		for(auto i:t[rt]) i.col?bla.merge(i.u-n*n,i.v-n*n):whi.merge(i.u,i.v);
		if(l==r)
		{
			if(l) printf("%d %d\n",n*n-cnt[l]-bla.cnt,cnt[l]-whi.cnt);
			bla.del(top1);whi.del(top2);
			return ;
		}
		int mid=(l+r)>>1;
		solve(lc,l,mid);solve(rc,mid+1,r);
		bla.del(top1);whi.del(top2);
	}
	
	void work()
	{
		int x,y,c;
		memset(lst,-1,sizeof(lst));memset(col,-1,sizeof(col));
		n=read();bla.init(n*n);whi.init(n*n);
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				col[i][j]=read();
				cnt[0]+=col[i][j]==0;
				if(i!=n) add(id(0,i,j),id(0,i+1,j)),add(id(1,i,j),id(1,i+1,j));
				if(j!=n) add(id(0,i,j),id(0,i,j+1)),add(id(1,i,j),id(1,i,j+1));
			}
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(i!=n&&col[i][j]==col[i+1][j]) link(id(col[i][j],i,j),id(col[i+1][j],i+1,j),0);
				if(j!=n&&col[i][j]==col[i][j+1]) link(id(col[i][j],i,j),id(col[i][j+1],i,j+1),0);
			}
		}
		m=read();
		for(int i=1;i<=m;i++)
		{
			x=read();y=read();c=col[x][y];cnt[i]=cnt[i-1];
			for(int j=0;j<4;j++)
			{
				int tx=x+dx[j],ty=y+dy[j];
				if(col[tx][ty]==c) cut(c,id(c,x,y),id(c,tx,ty),i);
			}
			cnt[i]-=c==0;
			c^=1;col[x][y]^=1;
			cnt[i]+=c==0;
			for(int j=0;j<4;j++)
			{
				int tx=x+dx[j],ty=y+dy[j];
				if(col[tx][ty]==c) link(id(c,x,y),id(c,tx,ty),i);
			}
		}
		for(int i=1;i<=tot;i++) if(lst[i]!=-1) cut(frm[i]>n*n,frm[i],to[i],m+1);
		solve(1,0,m);
	}

}

int main()
{
	zzc::work();
	return 0;
}

posted @ 2021-01-14 16:36  youth518  阅读(93)  评论(0编辑  收藏  举报