题目分析
首先发现,如果我们把蓝色看作1,把红色看作0(不过红配蓝什么的真的丑….),那么对于每一个格子a(i,j),都有:a(i,j)^a(i+1,j)^a(i,j+1)^a(i+1,j+1)=1
那么只要第一列和第一行确定了如何染色,整个表格的染色就确定了。(这是显然的,因为一个
2∗2
的格子中有三个被染色了,剩下的格子颜色已定。)
现在我们把上面那个式子记作S(i,j),则我们对于每一个(i,j),求一次s(1,1)^s(1,2)^…^s(i-1,j-1),这个过程数学感不好的人(比如本蒟蒻),就要崩溃了。不过我们可以形象的想一想,相当于在一个
i∗j
的格子中用一块
2∗2
的小毯子不停地移位覆盖,那么被盖了偶数次的一个a(x,y),就会发生a(x,y)^a(x,y)=0这样的事故,而0^0=0,0^1=1,所以就相当于消除了。那么被盖了奇数次的格子只有a(1,1),a(i,1),a(1,j)和a(i,j),因此,a(1,1)^a(i,1)^a(1,j)^a(i,j)在i和j均为偶数的时候(此时
(i−1)∗(j−1)
是奇数,s的异或前缀和是1)为1,否则为0.
那么我们可以枚举a(1,1)的值,然后因为a(i,j)的值和a(1,1)的异同关系,a(i,1)和a(1,j)的异同关系可以确定,用带权并查集维护所有有关系的点a(i,1)或a(1,j),然后有多少个集,答案就有2的多少次方的贡献。当然还要注意一点,有些集因为题目给出的条件所以已经确定了所有元素是1还是0,所以这些集对答案的贡献是1不是2.
代码实现
虽然题目分析已经很明了了,但是代码实现不够简洁,就容易如boshi大佬一样怀疑人生,怀疑鲁迅,怀疑枣树。
以下两个操作可以简洁代码:
1.如果a(1,1)=0,直接操作,如果枚举到a(1,1)=1,则把除了第一行和第一列以外的颜色都反过来即可。
2.带权并查集,通过异或等位运算操作进行判断,来简洁代码。给对于位运算操作不是很熟悉的人(比如本蒟蒻)的提醒:对于1和0,如果异或0,则不变,如果异或1,则发生变化。
还有本蒟蒻因为没取模WA了两次QAQ
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<climits>
using namespace std;
#define LL long long
LL mod=1000000000;
const int N=100005;
int n,m,T;
int x[N],y[N],w[N],f[N<<1],bj[N<<1],d[N<<1];
LL ans;
int find(int x){//带权并查集维护异同关系
if(f[x]==x)return x;
int t=f[x];
f[x]=find(f[x]),d[x]=(d[x]+d[t])%2;
return f[x];
}
LL work(){
int i,r1,r2,t;LL re=1;
for(i=1;i<=n+m;++i)f[i]=i,d[i]=0,bj[i]=-1;//bj[i]=-1表示这个集里的元素没有确定,0表示根节点取0,1表示根节点取1;d[i]=1表示i和根节点值不同,等于0表示相同。
f[n+1]=1,bj[1]=0;
for(i=1;i<=T;++i){
if(x[i]==1&&y[i]==1)continue;//特判
r1=find(x[i]),r2=find(y[i]+n);
if(r1!=r2){//连接
f[r1]=r2,d[r1]=(d[y[i]+n]-d[x[i]]+w[i]+4)%2;
if(bj[r1]!=-1&&bj[r2]!=-1&&bj[r2]!=bj[r1]^d[r1])return 0;
if(bj[r2]==-1&&bj[r1]!=-1)bj[r2]=bj[r1]^d[r1],bj[r1]=-1;
}
else if(d[x[i]]^d[y[i]+n]^w[i]==1)return 0;//判断可行性
}
for(i=1;i<=n+m;++i)if(find(i)==i&&bj[i]==-1)re=(re<<1)%mod;//注意取模
return re;
}
int main(){
int i,j,flag=-1;
scanf("%d%d%d",&n,&m,&T);
for(i=1;i<=T;++i){
scanf("%d%d%d",&x[i],&y[i],&w[i]);
if(x[i]==1&&y[i]==1)flag=w[i];
if(!(x[i]&1)&&!(y[i]&1))w[i]^=1;
}
if(flag==-1||flag==0)ans+=work();
if(flag==-1||flag==1){
for(i=1;i<=T;++i)//反过来后可以继续当a[1][1]=0
if(x[i]!=1&&y[i]!=1)w[i]^=1;
ans+=work();
}
ans%=mod//注意取模
printf("%lld",ans);//我是不是很可爱啊QWQ?
return 0;
}