您的位置:首页 > 其它

[BZOJ2303][Apio2011]方格染色(数学相关+加权并查集)

2017-04-28 20:12 253 查看

题目描述

传送门

题目大意:一个n*m的网格染成红蓝两种颜色,要求每个田字格有1个或3个红色的。现在有一些格子已经染了颜色,问有多少合法的染色方案。

题解

和这题gang了一天。。。

这题的关键在于,将每一个限制点的关系,转化成和第一行第一列的关系

若把颜色标记为0/1,那么每个田字格的异或值为1。首先枚举(1,1)的取值。考虑一个限制点(x,y)(x>1,y>1),如果x,y都为偶数,那么(1,1)..(x,y)范围内共有奇数个田字格,总异或值为1;否则有偶数个田字格,总异或值为0。并且在范围内,除(1,1),(x,1),(1,y)和(x,y),其余的点都被田字格覆盖了偶数次,所以根据(1,1)^(x,1)^(1,y)^(x,y),就能得到(x,1)与(1,y)的关系。如果限制点在第一行和第一列就可以直接得到这个限制点和(1,1)点的关系。

维护一个加权并查集,每个点的权值为这个点与其代表元素的关系,0相等1不相等。然后对于可以建立关系的两个点在并查集中合并、判断无解。最终,这一次的贡献与并查集中的连通块个数p有关,为2p−1,其中减掉的一个表示(1,1)所在的连通块(取值已经确定)

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define Mod 1000000000
#define N 2000005
#define LL long long

int n,m,k;
LL ans;
int c
,f
,g
;
struct data{int x,y,z;}p
;

int find(int x)
{
if (x==f[x]) return x;
int t=find(f[x]);
g[x]^=g[f[x]];
return f[x]=t;
}
LL fast_pow(LL a,int p)
{
LL ans=1;
for (;p;p>>=1,a=a*a%Mod)
if (p&1)
ans=ans*a%Mod;
return ans;
}
LL calc(int opt)
{
for (int i=1;i<=n+m;++i) f[i]=i,g[i]=0;
f[n+1]=1;
if (opt==1)
{
for (int i=1;i<=k;++i)
if (p[i].x>1&&p[i].y>1) p[i].z^=1;
}
for (int i=1;i<=k;++i)
{
int x=p[i].x,y=p[i].y,z=p[i].z;
if (x==1&&y==1) continue;
int fx=find(x),fy=find(n+y),ty=g[x]^g[y+n]^z;
if (fx!=fy) f[fy]=fx,g[fy]=ty;
else if (ty) return 0;
}
int size=0;
for (int i=1;i<=n+m;++i)
if (i==find(i))
++size;
return fast_pow(2,size-1);
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
int flag=-1;
for (int i=1;i<=k;++i)
{
int x,y,z;scanf("%d%d%d",&x,&y,&z);
if (x%2==0&&y%2==0) z^=1;
if (x==1&&y==1)
{
if (flag!=-1&&flag!=z)
{
puts("0");
return 0;
}
flag=z;
}
p[i].x=x,p[i].y=y,p[i].z=z;
}
if (flag!=-1) ans=(ans+calc(flag))%Mod;
else
{
ans=(ans+calc(0))%Mod;
ans=(ans+calc(1))%Mod;
}
printf("%lld\n",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: