您的位置:首页 > 其它

[杂题 异或 带权并查集] BZOJ2303: [Apio2011]方格染色

2017-10-31 14:16 573 查看
不太容易想到。首先要把限制看成异或,即每个点都要满足

ai,j xor ai,j−1 xor ai−1,j xor ai−1,j−1=1

这些限制太复杂了,怎么进行转化呢?

注意到,如果我们已经确定的第一行和第一列的所有元素,则其他也确定了。所以我们应该可以把限制都变成和第一行和第一列有关。

我们对于一个点 x,y 把所有在其左上范围的等式异或起来。得到:

a1,1 xor ax,1 xor a1,y xor ax,y=((x−1)∗(y−1)) and 1

可以想到,我们枚举 a1,1 的值,那么对于每个 ax,y=val 的限制,都可以转化为 ax,1 xor a1,y=c 。注意若 x=1 或 y=1,就还是看作单个元素的限制。

这样问题就变成,有一些01变量,给你一些两个数异或值的限制,和单个元素的值的限制,求合法方案数。

直接在有限制的点间连边,每个联通块的方案只可能是 0,1,2,最后相乘。

用带权并查集写起来会很方便。

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=2000010,MOD=1e9;
typedef long long LL;
int n,m,K,a_11;
struct data{ int x,y,val; } a[maxn];
int fa[maxn],xorfa[maxn],ans;
int getfa(int x){
if(fa[x]==x) return x;
int _fa=fa[x]; fa[x]=getfa(fa[x]);
xorfa[x]^=xorfa[_fa]; return fa[x];
}
void Solve(){
for(int i=1;i<=n+m-1;i++) fa[i]=i, xorfa[i]=0;
for(int i=1;i<=K;i++){
if(a[i].x==1&&a[i].y==1) continue;
int x=a[i].x, y=(a[i].y==1?1:n+a[i].y-1), fax=getfa(x), fay=getfa(y), t=a_11^a[i].val^((a[i].x&1)==0&&(a[i].y&1)==0);
//printf("%d ^ %d = %d\n",x,y,t);
if(fax==fay){
if((xorfa[x]^xorfa[y])!=t) return;
} else fa[fax]=fay, xorfa[fax]=xorfa[x]^xorfa[y]^t;
}
LL res=1;
for(int i=1;i<=n+m-1;i++) if(getfa(i)==i&&getfa(1)!=i) res=(res*2)%MOD;
(ans+=res)%=MOD;
}
int main(){
freopen("bzoj2303.in","r",stdin);
freopen("bzoj2303.out","w",stdout);
scanf("%d%d%d",&n,&m,&K);
a_11=-1;
for(int i=1;i<=K;i++){
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].val);
if(a[i].x==1&&a[i].y==1){
if(a_11!=-1&&a_11!=a[i].val) return printf("0"),0;
a_11=a[i].val;
}
}
if(a_11!=-1) Solve(); else{
a_11=0; Solve(); //printf("\n");
a_11=1; Solve();
}
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: