链接:
#include <stdio.h>
int main()
{
puts("转载请注明出处[vmurder]谢谢");
puts("网址:blog.csdn.net/vmurder/article/details/45081583");
}
题解:
首先我们发现对于
ai,j
有下列式子:
ai,j xor ai+1,j xor ai,j+1 xor ai+1,j+1==1
然后推导得到对于
ai,j
有下列式子:
a1,1 xor a1,j xor ai,1 xor ai,j==!(i,j均为偶数)
然后显然如果我们先涂上第一行和第一列,剩下的点的染色方案是唯一的。
而这些已经的填涂
(i,j)
则成了
(1,j)
和
(i,1)
这俩点之间的限制,即方程四点异或和确定。
我们可以先枚举
(1,1)
是
0
还是
然后会剩下
x
个连通块,刨去
实现:
一些定义:
一个被提前染色了的点有染色权值为其染的色,而如果这个点横纵坐标皆为偶数,则权值取反。
首先说左上角为
0
时的答案计算:
对于点
【原因:如果权值为1,左上角还是0,那么俩点最终染色必须不一样对吧,反之亦然】
对于其它情况,即点在第一行或第一列上,我们认为这是在表示此点和点
(1,1)
的关系,处理方式可以与上一种情况相同(甚至在我的实现下代码也是相同哒,不用特殊考虑!)
【原因:如果权值为1,表示与
(0,0)
目前
“0”
的染色不同。】
然后对于左上角是
1
时的答案计算:
我们只需要把所有点的染色权值取反,然后推一下发现正好可以跟上述做相同处理。
也就是直接调用上述流程就可以啦!!
特殊问题:
如果点
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 1001000
#define mod 1000000000
#define A e[i].x
#define B e[i].y+n
#define C e[i].z
using namespace std;
struct Eli
{
int x,y,z;
bool read()
{
scanf("%d%d%d",&x,&y,&z);
if(x==1&&y==1)return 1;
if(!((x|y)&1))z^=1;
return 0;
}
}e[N];
int f[N<<1];
bool g[N<<1];
int find(int x)
{
if(f[x]==x)return x;
find(f[x]);
g[x]^=g[f[x]];
return f[x]=f[f[x]];
}
long long power(long long x,long long p)
{
long long ret=1;
while(p)
{
if(p&1)ret=(ret*x)%mod;
x=(x*x)%mod,p>>=1;
}
return ret;
}
int n,m,p;
long long getans()
{
int i,fa,fb,t;
for(i=1;i<=n+m;i++)f[i]=i,g[i]=0;
f[n+1]=1;
for(i=1;i<=p;i++)
{
fa=find(A),fb=find(B),t=g[A]^g[B]^C;
if(fa!=fb)f[fb]=fa,g[fb]=t;
else if(t)return 0;
}
long long ans=0;
if(i>p)for(ans=0,i=1;i<=n+m;i++)
if(find(i)==i)ans++;
return power(2,ans-1);
}
bool f1=1,f2=1;
long long ans1,ans2;
int main()
{
scanf("%d%d%d",&n,&m,&p);
for(int i=1;i<=p;i++)
{
if(e[i].read())
{
if(e[i].z)f1=0;
else f2=0;
i--,p--;
}
}
if(f1)ans1=getans();
if(f2)
{
for(int i=1;i<=p;i++)
if(e[i].x>1&&e[i].y>1)e[i].z^=1;
ans2=getans();
}
cout<<(ans1+ans2)%mod<<endl;
return 0;
}