BZOJ 2303: [Apio2011]方格染色 题解

时间:2024-09-14 18:08:14

题目大意:

  有n*m的方格,中间的数要么是1,要么是0,要求任意2*2的方格中的数异或和为1。已知一部分格子中的数,求合法的填数的方案数。

思路:

  由题意得:a[i][j]^a[i][j+1]^a[i+1][j]^a[i+1][j+1]=1,令这个式子为S(i,j),那么对于某一格(i,j),我们把S(1,1)...S(i,j)异或起来,则可得当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][j]=0。可以枚举a[1][1]的值再由a[i][j]得到a[i][1]与a[1][j]的相同情况(可默认所有数均为0),于是相同就用并查集并起来。若有(n+1)个联通块则有$2^n$种方案(因为与a[1][1]相同的已经确定了)。

代码:

 #include<cstdio>
#include<iostream>
using namespace std;
const int M=,mo=;
int n,m,i,k,sum,u,v,t,ans,x[M],y[M],z[M],g[M],fa[M]; int read()
{
int x=;
char ch=getchar();
while (ch<'' || ch>'') ch=getchar();
while (ch>='' && ch<='') x=(x<<)+(x<<)+ch-,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 wk()
{
for (i=;i<=n+m;i++) fa[i]=i,g[i]=;
for (fa[n+]=i=;i<=k;i++)
{
u=getfa(x[i]),v=getfa(y[i]+n),t=g[x[i]]^g[y[i]+n]^z[i];
if (u!=v) fa[u]=v,g[u]=t;
else if (t) return ;
}
for (sum=-,i=;i<=n+m;i++)
if (getfa(i)==i)
if (sum==-) sum=;
else if ((sum<<=)>=mo) sum-=mo;
return sum;
} int main()
{
bool flag[]={,};
n=read(),m=read(),k=read();
for (i=;i<=k;i++)
{
x[i]=read(),y[i]=read(),z[i]=read();
if (x[i]+y[i]==) { flag[z[i]]=,i--,k--; continue; }
if (!((x[i]|y[i])&)) z[i]^=;
}
if (flag[]) ans=wk();
if (flag[])
{
for (i=;i<=k;i++)
if (x[i]> && y[i]>) z[i]^=;
if ((ans+=wk())>=mo) ans-=mo;
}
printf("%d\n",ans);
return ;
}