[APIO2011] 方格染色

一、题目

点此看题

二、解法

其实这题相当于给了若干个等式让你求解的个数,思路是我们找出自由变元

然后这个问题的背景又是矩阵,那么我们自由变元通常出现在边界上。

不难发现我们确定第一行和第一列之后整个矩阵就确定了,那么第一行第一列就是自由变元。

再考虑这道题给定了几个位置必须染某颜色,假设是 \((x,y)\) 必须染颜色 \(c\),我们可以通过取 \([1,1,x,y]\) 这个矩阵中所有的 \(2\times 2\) 的小矩形来列方程,发现除了 \((1,1),(1,x),(1,y),(x,y)\) 的元素都被消掉了,那么有 \(c(1,1)\oplus c(1,x)\oplus c(1,y)\oplus c(x,y)=[x\%2=0\&y\%2=0]\)

那么我们可以枚举 \((1,1)\) 的颜色,就转化成了 \((1,x)\)\((1,y)\) 的关系,这可以用带权并查集维护。

但是 \(x=1/y=1\) 也是有可能的,这时候我们在每个并查集的根上打个标记,表示根的颜色是否确定。最后的答案是二的根不定颜色的连通块个数次方。

三、总结

要有解方程的思想,虽然你不用真的把他解出来,列方程是表示关系的重要方式。

#include <cstdio>
const int M = 200005;
const int MOD = 1e9;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,fl,ans,fa[M],x[M],y[M],b[M],c[M],w[M];
int find(int x)
{
	if(x!=fa[x])
	{
		int t=fa[x];
		fa[x]=find(fa[x]);
		w[x]^=w[t];
	}
	return fa[x];
}
int work()
{
	int res=1;
	for(int i=1;i<=n+m;i++)
		fa[i]=i,b[i]=-1,w[i]=0;
	fa[n+1]=1;b[1]=0;
	for(int i=1;i<=k;i++)
	{
		if(x[i]==1 && y[i]==1) continue;
		if(x[i]==1 || y[i]==1)
		{
			int u=x[i]==1?y[i]+n:x[i],t=find(u);
			if(b[t]==-1) b[t]=w[u]^c[i];
			else if(b[t]!=w[u]^c[i]) return 0;
			continue;
		}
		int u=find(x[i]),v=find(y[i]+n);
		if(u==v)
		{
			if(w[x[i]]^w[y[i]+n]!=c[i]) return 0;
			continue;
		}
		fa[u]=v;w[u]=w[x[i]]^w[y[i]+n]^c[i];
		if(b[u]!=-1 && b[v]!=-1)
			{if(b[u]^b[v]!=w[u]) return 0;}
		else if(b[u]!=-1) b[v]=b[u]^w[u];
	}
	for(int i=2;i<=n+m;i++)
		if(i==find(i) && b[i]==-1)
			res=res*2%MOD;
	return res;
}
signed main()
{
	n=read();m=read();k=read();fl=-1;
	for(int i=1;i<=k;i++)
	{
		x[i]=read();y[i]=read();c[i]=read();
		if(x[i]==1 && y[i]==1) fl=c[i];
		if(x[i]%2==0 && y[i]%2==0) c[i]^=1;
	}
	if(fl==-1 || fl==0) ans+=work();
	if(fl==-1 || fl==1)
	{
		for(int i=1;i<=k;i++) c[i]^=1;
		ans+=work();
	}
	printf("%lld\n",ans%MOD);
}
posted @ 2021-08-17 20:23  C202044zxy  阅读(90)  评论(0编辑  收藏  举报