BZOJ1453: [Wc]Dface双面棋盘

BZOJ1453: [Wc]Dface双面棋盘

Description

Input

Output

Sample Input


Sample Output


HINT

 


题解Here!
这题是本蒟蒻接触$LCT$以来见过的最毒瘤的一题。。。
于是我两个晚上就这么搭进去了。。。
首先这题可以线段树搞。

用线段树维护每行的同色块,每个节点套并查集。

记录最上方和最下方的联通性,暴力合并+暴力修改并查集,并记录答案。

并查集的合并比较恶心。。。

用并查集中的$[1,n]$表示上方,$[n+1,2n]$表示下方。

合并时$[2n+1,4n]$用于右侧的上下方并查集。

话说我的线段树常数炒鸡大,卡了好久才卡过去的。。。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<cstring>
#define LSON rt<<1
#define RSON rt<<1|1
#define DATA(x) a[x].data
#define WHITE(x) a[x].white
#define BLACK(x) a[x].black
#define LSIDE(x) a[x].l
#define RSIDE(x) a[x].r
#define MAXN 210
using namespace std;
int n,m;
int chess[MAXN][MAXN],map[MAXN<<2];
struct Set{
	int father[MAXN<<2];
	inline void init(){for(int i=0;i<=(n<<2);i++)father[i]=i;}
	int find(int x){return father[x]==x?x:father[x]=find(father[x]);}
	inline void uniun(int x,int y){father[find(x)]=find(y);}
	inline bool check(int x,int y){return (find(x)==find(y));}
};
struct Segment_Tree{
	Set data;
	int white,black,l,r;
}a[MAXN<<2];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
void pushup(int rt){
	int mid=LSIDE(rt)+RSIDE(rt)>>1;
	WHITE(rt)=WHITE(LSON)+WHITE(RSON);
	BLACK(rt)=BLACK(LSON)+BLACK(RSON);
	DATA(rt).init();
	for(int i=1;i<=(n<<1);i++){
		DATA(rt).uniun(i,DATA(LSON).find(i));
		DATA(rt).uniun(i+(n<<1),DATA(RSON).find(i)+(n<<1));
	}
	for(int i=1;i<=n;i++)
	if(chess[mid][i]==chess[mid+1][i]){
		if(DATA(rt).check(i+n,i+(n<<1)))continue;
		DATA(rt).uniun(i+n,i+(n<<1));
		WHITE(rt)-=chess[mid][i]^1;
		BLACK(rt)-=chess[mid][i];
	}
	for(int i=1;i<=(n<<2);i++){
		DATA(rt).find(i);
		map[i]=0;
	}
	for(int i=1;i<=n;i++){
		if(!map[DATA(rt).father[i]]){
			map[DATA(rt).father[i]]=i;
			DATA(rt).father[i]=i;
		}
		else DATA(rt).father[i]=map[DATA(rt).father[i]];
	}
	for(int i=n*3+1;i<=(n<<2);i++){
		if(!map[DATA(rt).father[i]]){
			map[DATA(rt).father[i]]=i-(n<<1);
			DATA(rt).father[i]=i-(n<<1);
		}
		else DATA(rt).father[i]=map[DATA(rt).father[i]];
	}
	for(int i=1;i<=n;i++)DATA(rt).father[i+n]=DATA(rt).father[i+n*3];
	for(int i=(n<<1)+1;i<=(n<<2);i++)DATA(rt).father[i]=i;
}
void buildtree(int l,int r,int rt){
	LSIDE(rt)=l;RSIDE(rt)=r;
	if(l==r){
		WHITE(rt)=chess[l][1]^1;
		BLACK(rt)=chess[l][1];
		DATA(rt).init();
		DATA(rt).uniun(1+n,1);
		for(int i=2;i<=n;i++){
			DATA(rt).uniun(i+n,i);
			if(chess[l][i-1]==chess[l][i])DATA(rt).uniun(i,i-1);
			else{
				WHITE(rt)+=chess[l][i]^1;
				BLACK(rt)+=chess[l][i];
			}
		}
		return;
	}
	int mid=l+r>>1;
	buildtree(l,mid,LSON);
	buildtree(mid+1,r,RSON);
	pushup(rt);
}
void update(int k,int rt){
	if(LSIDE(rt)==RSIDE(rt)){
		WHITE(rt)=chess[k][1]^1;
		BLACK(rt)=chess[k][1];
		DATA(rt).init();
		DATA(rt).uniun(1+n,1);
		for(int i=2;i<=n;i++){
			DATA(rt).uniun(i+n,i);
			if(chess[k][i-1]==chess[k][i])DATA(rt).uniun(i,i-1);
			else{
				WHITE(rt)+=chess[k][i]^1;
				BLACK(rt)+=chess[k][i];
			}
		}
		return;
	}
	int mid=LSIDE(rt)+RSIDE(rt)>>1;
	if(k<=mid)update(k,LSON);
	else update(k,RSON);
	pushup(rt);
}
int main(){
	int x,y;
	n=read();
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	chess[i][j]=read();
	buildtree(1,n,1);
	m=read();
	while(m--){
		x=read();y=read();
		chess[x][y]^=1;
		update(x,1);
		printf("%d %d\n",BLACK(1),WHITE(1));
	}
    return 0;
}

 


然后当然是毒瘤的离线$LCT$辣!

我们可以将每个格子看做一个点, 与旁边格子有四条边相连。

处理的过程中显然会遇到环的情况, 怎么处理呢?

同样, 我们维护有关删除时间的最大生成树, 在加入边的时候判断是否为环。

如果成为环, 则与原路径中删除时间最早的边比较, 若新加入边的删除时间更晚则删除之前的那条边。

至于更新色块的个数, 我们考虑格子四个方向的连边:

如果原来颜色相同则删边, 颜色不同则连边。

因为我们保证没有环且保证路径上点删除时间尽量大, 所以我们可以用LCT维护联通信息, 只需判断是否在一棵树内即可。

不过此题离线实在恶心。。。

想想我们需要离线什么?

首先, 我们要预处理一开始边的连通性及初始答案。

其次, 我们要离线所有的修改操作, 分为四个方向考虑。

然后我们还要处理每条边删除的时间, 并以此为权值在$LCT$中连边。

注意还会有多次加入的情况。。。

然后就是一大堆细节问题了。。。

本蒟蒻已经将毁天灭地的所有调试代码删去了,可放心食用。

$namespace$封装看不习惯不要打我。。。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAXN 40010
#define MAXM 210
#define MAX 2147483646
using namespace std;
const int fx[4]={1,-1,0,0},fy[4]={0,0,1,-1};
int n,m;
int ans[2],chess[MAXM][MAXM],backup[MAXM][MAXM];
struct Question{
	int x,y;
}que[MAXN];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
namespace MAP{
	int num=0;
	int cut[MAXN<<3];
	struct Edge{
		int x,y,start,end,id,colour;
		bool flag;
		friend bool operator <(const Edge &p,const Edge &q){
			if(p.start==q.start)return p.flag<q.flag;
			return p.start<q.start;
		}
	}a[MAXN<<3];
	inline int point_id(int x,int y){return (x-1)*n+y;}
	inline int edge_id(int x1,int y1,int x2,int y2){
		if(x1==x2){
			if(y1>y2)swap(y1,y2);
			return (x1-1)*(n-1)+y1;
		}
		else{
			if(x1>x2)swap(x1,x2);
			return n*(n-1)+(x1-1)*n+y1;
		}
	}
	inline void add_edge(int x,int y,int start,int id,int colour,bool flag){
		num++;
		a[num].x=x;a[num].y=y;a[num].start=start;a[num].id=id;a[num].colour=colour;
		a[num].flag=flag;
	}
	inline bool check(int x,int y){
		if(x<1||x>n||y<1||y>n)return true;
		return false;
	}
	inline void build(){
		sort(a+1,a+num+1);
		memset(cut,-1,sizeof(cut));
	}
}
namespace LCT{
	int top=0,stack[MAXN<<4],left[MAXN<<3],right[MAXN<<3];
	struct Link_Cut_Tree{
		int f,flag,son[2];
		int val,minn,pos;
		bool cut;
	}a[MAXN<<4];
	inline bool isroot(int rt){
		return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt;
	}
	inline void clean(int rt){
		a[rt].son[0]=a[rt].son[1]=a[rt].f=a[rt].flag=a[rt].pos=a[rt].cut=0;
		a[rt].val=a[rt].minn=MAX;
	}
	inline void pushup(int rt){
		if(!rt)return;
		a[rt].minn=a[rt].val;a[rt].pos=rt;
		if(a[rt].son[0]&&a[a[rt].son[0]].minn<a[rt].minn){a[rt].minn=a[a[rt].son[0]].minn;a[rt].pos=a[a[rt].son[0]].pos;}
		if(a[rt].son[1]&&a[a[rt].son[1]].minn<a[rt].minn){a[rt].minn=a[a[rt].son[1]].minn;a[rt].pos=a[a[rt].son[1]].pos;}
	}
	inline void pushdown(int rt){
		if(!rt||!a[rt].flag)return;
		a[a[rt].son[0]].flag^=1;a[a[rt].son[1]].flag^=1;a[rt].flag^=1;
		swap(a[rt].son[0],a[rt].son[1]);
	}
	inline void turn(int rt){
		int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0;
		if(!isroot(x)){
			if(a[y].son[0]==x)a[y].son[0]=rt;
			else a[y].son[1]=rt;
		}
		a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x;
		a[x].son[k^1]=a[rt].son[k];a[rt].son[k]=x;
		pushup(x);pushup(rt);
	}
	void splay(int rt){
		top=0;
		stack[++top]=rt;
		for(int i=rt;!isroot(i);i=a[i].f)stack[++top]=a[i].f;
		while(top)pushdown(stack[top--]);
		while(!isroot(rt)){
			int x=a[rt].f,y=a[x].f;
			if(!isroot(x)){
				if((a[y].son[0]==x)^(a[x].son[0]==rt))turn(rt);
				else turn(x);
			}
			turn(rt);
		}
	}
	inline void access(int rt){
		for(int i=0;rt;i=rt,rt=a[rt].f){
			splay(rt);
			a[rt].son[1]=i;
			pushup(rt);
		}
	}
	inline void makeroot(int rt){access(rt);splay(rt);a[rt].flag^=1;}
	int findroot(int rt){
		access(rt);splay(rt);
		while(a[rt].son[0])rt=a[rt].son[0];
		return rt;
	}
	inline void split(int x,int y){makeroot(x);access(y);splay(y);}
	inline void link(int x,int y){makeroot(x);a[x].f=y;}
	inline void cut(int x,int y){
		split(x,y);
		if(a[y].son[0]==x&&a[x].f==y&&!a[x].son[1])a[x].f=a[y].son[0]=0;
	}
	inline void add_edge(int rt,int colour){
		int x=MAP::a[rt].x,y=MAP::a[rt].y,end=MAP::a[rt].end,id=MAP::a[rt].id;
		if(findroot(y)==findroot(x)){
			split(x,y);
			if(a[y].minn>end)return;
			int pos=a[y].pos;
			cut(pos,left[pos]);cut(pos,right[pos]);
			clean(pos);
		}
		else ans[colour]--;
		id+=MAXN-10;
		a[id].val=end;a[id].cut=true;a[id].pos=id;
		left[id]=x;right[id]=y;
		link(id,x);link(id,y);
		pushup(id);
	}
	inline void del_edge(int rt){
		int x=MAP::a[rt].x,y=MAP::a[rt].y,id=MAP::a[rt].id;
		id+=MAXN-10;
		cut(id,x);cut(id,y);
		a[id].cut=false;
	}
}
void work(){
	int now;
	for(now=1;now<=MAP::num&&!MAP::a[now].start;now++)LCT::add_edge(now,MAP::a[now].colour);
	for(int i=1;i<=m;i++){
		int x=chess[que[i].x][que[i].y];
		for(;now<=MAP::num&&MAP::a[now].start<=i;now++){
			if(!MAP::a[now].flag){
				if(!LCT::a[MAP::a[now].id+MAXN-10].cut)continue;
				LCT::del_edge(now);
				ans[x]++;
			}
			else LCT::add_edge(now,x^1);
		}
		ans[x]--;ans[x^1]++;
		chess[que[i].x][que[i].y]^=1;
		printf("%d %d\n",ans[1],ans[0]);
	}
}
void init(){
	int x,y,u,v,id;
	n=read();
	ans[0]=ans[1]=0;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++){
		chess[i][j]=backup[i][j]=read();
		ans[chess[i][j]]++;
	}
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++){
		x=MAP::point_id(i,j);
		if(j!=n&&chess[i][j]==chess[i][j+1])MAP::add_edge(x,x+1,0,MAP::edge_id(i,j,i,j+1),chess[i][j],true);
		if(i!=n&&chess[i][j]==chess[i+1][j])MAP::add_edge(x,x+n,0,MAP::edge_id(i,j,i+1,j),chess[i][j],true);
	}
	m=read();
	for(int i=1;i<=m;i++){
		que[i].x=read();que[i].y=read();
		x=MAP::point_id(que[i].x,que[i].y);
		for(int k=0;k<4;k++){
			u=que[i].x+fx[k];v=que[i].y+fy[k];id=MAP::point_id(u,v);
			if(MAP::check(u,v))continue;
			if(backup[que[i].x][que[i].y]==backup[u][v])MAP::add_edge(x,id,i,MAP::edge_id(que[i].x,que[i].y,u,v),0,false);
			else MAP::add_edge(x,id,i,MAP::edge_id(que[i].x,que[i].y,u,v),0,true);
		}
		backup[que[i].x][que[i].y]^=1;
	}
	MAP::build();
	for(int i=1;i<=MAP::num;i++)MAP::a[i].end=m+1;
	for(int i=MAP::num;i>=1;i--){
		if(MAP::cut[MAP::a[i].id]!=-1)MAP::a[i].end=MAP::cut[MAP::a[i].id];
		MAP::cut[MAP::a[i].id]=MAP::a[i].start;
	}
	for(int i=1;i<=MAXN-10;i++)LCT::a[i].val=MAX;
}
int main(){
	init();
	work();
    return 0;
}

 

posted @ 2018-08-28 20:52  符拉迪沃斯托克  阅读(511)  评论(0编辑  收藏  举报
Live2D