[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);
}