bzoj 2303: [Apio2011]方格染色 并查集

       根据题目的条件可以列出方程为a[i][j]^a[i][j+1]^a[i+1][j]^a[i+1][j+1]=1,其中a[i][j]为1表示第(i,j)格染成红色。令这个式子为S(i,j),那么对于某一格(i,j),我们把S(1,1)...S(i-1,j-1)(也就是(i,j)左上的部分)异或以来,得到:

       a[1][1]^a[i][1]^a[1][j]^a[i][j]=1(i,j都为偶数)或0(其他情况)

       可以看到,只要确定了第一行和第一列就得到了所有的格子的情况(这是废话,n^2个变量(n-1)^2个方程自由变量当然有2n-1个了>_<,只不过取第一行和第一列的比较方便而已^_^)

       另外,既然我们能弄出这个方程,那么显然有解辣。。(当然又让a[i][j]=0又让a[i][j]=1的条件除外)

       然后题目中给出了部分a[i][j]的值,然么我们可以得到a[1][1]^a[i][1]^a[1][j]=a[i][j]^1,如果已经提前得到了a[1][1],那么就可以得到a[i][1]和a[1][j]的值,然后我们可以把a[i][1]和a[1][j]并到一个并查集里面(当然要先判断是否矛盾)。

       那么最后有几个连通块最后答案就是2^(连通块的个数-1),因为和a[1][1]相连的那个连通块只有一种选法。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define mod 1000000000
#define N 2000005
using namespace std;

struct node{ int x,y,z; }a[N]; int n,m,cnt,fa[N],g[N];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
int getfa(int x){
	if (x==fa[x]) return x; int t=getfa(fa[x]);
	g[x]^=g[fa[x]]; return fa[x]=t;
}
int calc(){
	int i,u,v,tmp;
	for (i=1; i<=m+n; i++){ fa[i]=i; g[i]=0; }
	fa[m+1]=1;
	for (i=1; i<=cnt; i++){
		u=getfa(a[i].x); v=getfa(a[i].y+m);
		tmp=g[a[i].x]^g[a[i].y+m]^a[i].z;
		if (u!=v){ fa[u]=v; g[u]=tmp; }
		else if (tmp) return 0;
	}
	int ans=-1;
	for (i=1; i<=m+n; i++)
		if (getfa(i)==i)
			if (ans==-1) ans=1; else{
				ans<<=1; if (ans>=mod) ans-=mod;
			}
	return ans;
}
int main(){
	m=read(); n=read(); cnt=read(); int i; bool bo[2]; bo[0]=bo[1]=1;
	for (i=1; i<=cnt; i++){
		a[i].x=read(); a[i].y=read(); a[i].z=read();
		if (a[i].x+a[i].y==2){ bo[a[i].z]=0; i--; cnt--; continue; }
		if (!((a[i].x|a[i].y)&1)) a[i].z^=1;
	}
	int ans=0; if (bo[1]) ans=calc();
	if (bo[0]){
		for (i=1; i<=cnt; i++)
			if (a[i].x>1 && a[i].y>1) a[i].z^=1;
		ans+=calc();
	}
	if (ans>=mod) ans-=mod; printf("%d\n",ans);
	return 0;
}


by lych

2016.3.18

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值