• 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
    ",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
    ",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;
    }
    
  • 相关阅读:
    vue 使用 <iframe> 嵌入网页 地址实现动态配置
    vue 视频播放 vue-video-player
    vue 实现自定义序号, 并且翻页序号累加。
    关于 vue 使用 Handsontable 表格数据导出
    node.js Stream流的使用
    手把手教如何搭建node+egg项目 引入Sequelize
    实现 通过数据库里一个字段值相等 则把 他合为一条数据
    最近在项目中碰到把对象数组转为键值对,
    js 的数组怎么push一个对象. Js数组的操作push,pop,shift,unshift JavaScrip
    for循环
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9550759.html
Copyright © 2020-2023  润新知