您的位置:首页 > 其它

bzoj 2303: [Apio2011]方格染色 (并查集)

2017-04-28 20:13 459 查看

题目描述

传送门

题目大意:将每个方格都染成红色或蓝色。要表格中每个2 × 2的方形区域都包含奇数个(1 个或 3 个)红色方格。有些格子的颜色已经染好了,求给剩下的方格染上颜色,使得整个表格仍然满足要求的染色方案数

题解

对于每个一个2*2的表格,我们可以把条件表示成异或方程的形式。

对于每个位置(i,j),若填红色那么a[i][j]=1

S(i,j)=a[i][j]^a[i−1][j−1]^ a[i−1][j]^a[i][j−1]=1

然后对于每个位置维护S(i,j)的前缀和,那么a[1][1]^a[1][j]^a[i][1]^a[i][j]=!(i,j都是偶数)

可以发现只有i,j都是偶数的时候,前缀中才有奇数个方程,而且只有a[1][1],a[1][j],a[i][1],a[i][j]出现了奇数次。

然后可以发现染色的方案其实只与第一行第一列的点有关系。

那么如果枚举(1,1)的取值,那么对于所有给出的(i,j),我们将式子变成a[1][j]^a[i][1]=!(i,j都是偶数)^a[i][j]^a[1][1]。如果右边的值为1,那么说明a[1][j],a[i][1]的取值相同,否则不同。那么如果我们用并查集将所有的关系连接的话,在一个连通块中的取值只要其中一个确定了剩余的取值都是唯一的。

那么答案其实就是2^(连通块个数-1),为什么要-1?因为(1,1)的值我们已经枚举过了,所以取值是位置确定的。

那么我们考虑如何用并查集连接,因为既有不等关系又有相等关系,所以我们可以维护加权并查集,g[i]表示i到代表元素是相等还是不相等,1表示不相等。那么每次合并的时候只有异或一下就好了,两个不等号不就又变成相等了嘛。另外我们在合并的时候还可以判断一下无解,如果两个东西在一个集合中我们可以得到他们之间的关系(相等或者不相等),如果和当前要连接的关系冲突,那么无解。

还有就是本身在第一行或者第一列中的点。我们可以直接将他们连到(1,1)上去,反正值已经确定了,如果取值相同g[i]=0,否则g[i]=1.

还要特判一下(1,1)是否被限制了,计算的时候需要特判。

对于一个点在第一行或者第一列,如果被限制了两次,且两次的值不同,那么需要特判掉无解的情况。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 2000003
#define LL long long
#define p 1000000000
using namespace std;
struct data{
int x,y,opt,v;
}a
;
int fa
,F
,n,m,k,mark
,col
,g
;
int find(int x)
{
if (fa[x]==x) return x;
int t=find(fa[x]);
g[x]^=g[fa[x]];
fa[x]=t;
return fa[x];
}
LL quickpow(int num,int x)
{
LL base=num; LL ans=1;
while (x){
if (x&1) ans=ans*base%p;
x>>=1;
base=base*base%p;
}
return ans;
}
int cmp(data a,data b)
{
return a.opt<b.opt||a.opt==b.opt&&a.v<b.v;
}
LL solve()
{
for (int i=1;i<=n+m;i++) fa[i]=i,g[i]=0;
fa[n+1]=1;
for (int i=1;i<=n+m;i++)
if (mark[i]) {
fa[i]=1;
if (col[1]==col[i]) g[i]=0;
else g[i]=1;
}
sort(a+1,a+k+1,cmp);int K=0;
for (int i=k;i>=1;i--)
if (!a[i].opt) {
K=i;
break;
}
for (int i=1;i<=K;i++) {
int r1=find(a[i].x); int r2=find(a[i].y+n);
int t=g[a[i].x]^g[a[i].y+n]^a[i].v;
if (r1!=r2) fa[r2]=r1,g[r2]=t;
else if (t) return 0;
}
int ans=0;
for (int i=1;i<=n+m;i++)
if (find(i)==i&&!mark[i]) ans++;
//cout<<ans<<endl;
return quickpow(2,max(0,ans-1));
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
bool p0,p1; p0=p1=true;
for (int i=1;i<=k;i++) {
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
if (a[i].x==1&&a[i].y==1) {
if (a[i].v) p0=false;
else p1=false;
a[i].opt=1;
}
else{
if (!(a[i].x&1)&&!(a[i].y&1))
a[i].v^=1;
if (a[i].x==1) {
if (!mark[a[i].y+n])mark[a[i].y+n]=1,col[a[i].y+n]=a[i].v,a[i].opt=1;
else {
if (col[a[i].y+n]!=a[i].v) {
printf("0\n");
return 0;
}else  a[i].opt=1;
}
}
if (a[i].y==1){
if (!mark[a[i].x]) mark[a[i].x]=1,col[a[i].x]=a[i].v,a[i].opt=1;
else{
if (col[a[i].x]!=a[i].v) {
printf("0\n");
return 0;
}else a[i].opt=1;
}
}
}
}
LL ans0=0,ans1=0;
if (p0) col[1]=0,ans0=solve();
if (p1) {
col[1]=1;
for (int i=1;i<=k;i++)
a[i].v^=1;
ans1=solve();
}
printf("%lld\n",(ans0+ans1)%p);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  并查集