[bzoj4171][高斯消元]Rhl的游戏

Description

RHL最近迷上一个小游戏:Flip it。游戏的规则很简单,在一个N*M的格子上,有一些格子是黑色,有一些是白色
。每选择一个格子按一次,格子以及周围边相邻的格子都会翻转颜色(边相邻指至少与该格子有一条公共边的格子
),黑变白,白变黑。RHL希望把所有格子都变成白色的。不幸的是,有一些格子坏掉了,无法被按下。这时,它 可以完成游戏吗?

Input

第一行一个整数T,表示T组数据。 每组数据开始于三个整数n,m,k,分别表示格子的高度和宽度、坏掉格子的个数。接下来的n行,每行一个长度m的
字符串,表示格子状态为’B’或’W’。最后k行,每行两个整数Xi,Yi(1≤Xi≤n,1≤Yi≤m),表示坏掉的格子。
n,m,k<=256,T<=10

Output

对于每组数据,先输出一行Case #i: (1≤i≤T) 如果可以成功,输出YES,否则输出NO。

Sample Input

2

3 3 0

WBW

BBB

WBW

3 3 2

WBW

BBB

WBW

2 2

3 2

Sample Output

Case #1:

YES

Case #2:

NO

题解

真的很经典的东西…
要记住
首先对于每个点,我们都可以列出一条异或方程
于是这就是一个有 n 2 n^2 n2个未知数, n 2 n^2 n2个方程的方程组
高斯消元过不去…
注意到是四连通,如果我们能确定第一行,显然第二行的填法是唯一的
同理,第三,第四,第五行直到第n行都是唯一的…
如此,我们可以推出每个点用第一行的点表示的异或组
然后用最后一行的点列异或方程
哦…有删去的点
那我们可以发现,这个点的异或方程解出来一定是0
那么暴力让周围四个点的异或和等于我这个点需要的次数即可
于是同样列 k k k个方程
高斯消元判一下有无解即可
此时只有 m m m个未知数和 n + k n+k n+k个方程,可以通过

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#include<iostream>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(LL x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const int MAXN=260;
const int ux[4]={-1,0,1,0};
const int uy[4]={0,-1,0,1};
const int dx[4]={-1,-1,-1,-2};
const int dy[4]={0,-1,1,0};
bitset<MAXN> bit[MAXN][MAXN];
bitset<MAXN> rw[2*MAXN];
int n,m,num[MAXN][MAXN],tot,K;
bool gauss(int n,int m)
{
	for(int i=1;i<=n;i++)
	{
		int u=-1;
		for(int j=i;j<=n;j++)if(rw[j][i]==1){u=j;break;}
		if(u==-1)continue;
		swap(rw[u],rw[i]);
		for(int j=i+1;j<=n;j++)
			if(rw[j][i])rw[j]^=rw[i];
	}
	for(int i=1;i<=n;i++)
	{
		bool tf=false;
		for(int j=1;j<=m;j++)if(rw[i][j]){tf=true;break;}
		if(!tf&&rw[i][m+1])return false;
	}
	return true;
}
bool vis[MAXN][MAXN];
char ch[MAXN][MAXN];
bool in(int x,int y){return x>=1&&x<=n&&y>=1&&y<=m;}
int main()
{
//	freopen("4171.in","r",stdin);
//	freopen("4171.out","w",stdout);
	int tt=0;
	int T=read();while(T--)
	{
		memset(num,0,sizeof(num));
		for(int i=1;i<=tot;i++)rw[i].reset();
		for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)bit[i][j].reset();
		n=read();m=read();K=read();
		for(int i=1;i<=n;i++)scanf("%s",ch[i]+1);
		for(int i=1;i<=m;i++)bit[1][i][i]=1;
		for(int i=2;i<=n;i++)for(int j=1;j<=m;j++)
		{
			for(int k=0;k<=3;k++)
				if(in(i+dx[k],j+dy[k]))
				{
					int x=i+dx[k],y=j+dy[k];
					bit[i][j]^=bit[x][y];num[i][j]^=num[x][y];
				}
			num[i][j]^=(ch[i-1][j]=='B');
//			printf("%d %d\n",i,j);
//			for(int k=1;k<=m;k++)cout<<bit[i][j][k];
//			puts("");
//			printf("%d\n",num[i][j]);
		}
		tot=0;
		for(int i=1;i<=m;i++)
		{
			tot++;int g=num[n][i];
			rw[tot]=bit[n][i];
			if(in(n,i-1))
			{
				g^=num[n][i-1];
				rw[tot]^=bit[n][i-1];
			}
			if(in(n,i+1))
			{
				g^=num[n][i+1];
				rw[tot]^=bit[n][i+1];
			}
			if(in(n-1,i))
			{
				g^=num[n-1][i];
				rw[tot]^=bit[n-1][i];
			}
			g^=(ch[n][i]=='B');
			rw[tot][m+1]=g;
//			printf("CHECKER %d\n",i);
//			for(int k=1;k<=m+1;k++)cout<<rw[tot][k];
//			puts("");
		}
		for(int i=1;i<=K;i++)
		{
			int x=read(),y=read();
			int g=0;tot++;
			for(int k=0;k<=3;k++)
				if(in(x+ux[k],y+uy[k]))
				{
					int u=x+ux[k],v=y+uy[k];
					rw[tot]^=bit[u][v];g^=num[u][v];
				}
			g^=(ch[x][y]=='B');
			rw[tot][m+1]=g;
		}
		printf("Case #%d:\n",++tt);
		if(gauss(tot,m))puts("YES");
		else puts("NO");
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值