【BZOJ】2303: [Apio2011]方格染色-异或&并查集

传送门:bzoj2303


题解

由区域内出现奇数次联想到异或,再转为并查集判联通
推荐一篇题解


代码

#include<cctype>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
#define fd(x) ((x)==1?1:(m+x-1))
const int N=1e6+10,mod=1e9;
typedef long long ll;

int n,m,K,a[N],b[N];
int f[N<<2],ss,pr=-1,lim,ans;
bool vs[N<<2],c[N];

char cp;
inline void rd(int &x)
{
	cp=getchar();x=0;
	for(;!isdigit(cp);cp=getchar());
	for(;isdigit(cp);cp=getchar()) x=(x<<3)+(x<<1)+(cp^48);
}

int getfa(int x){return x==f[x]?x:f[x]=getfa(f[x]);}

void merge(int x,int y,int z)
{
	if(z){
	   if(getfa(x)!=getfa(y+lim)) f[getfa(x)]=getfa(y+lim),ss--;
	   if(getfa(x+lim)!=getfa(y)) f[getfa(x+lim)]=getfa(y),ss--;
	}
	else{
	   if(getfa(x)!=getfa(y)) f[getfa(x)]=getfa(y),ss--;
	   if(getfa(x+lim)!=getfa(y+lim)) f[getfa(x+lim)]=getfa(y+lim),ss--;
	}
}

inline int fp(int x,int y)
{
	int re=1;
	for(;y;y>>=1,x=(ll)x*x%mod)
	  if(y&1) re=(ll)re*x%mod;
	return re;
}

inline void sol(int ty)
{
	int i;lim=n+m-1;ss=lim<<1;
	for(i=1;i<=ss;++i) f[i]=i;
	for(i=1;i<=K;++i) if(a[i]!=1 || b[i]!=1){
		if((!(a[i]&1))&&(!(b[i]&1))) merge(b[i],fd(a[i]),1^ty^c[i]);
		else merge(b[i],fd(a[i]),ty^c[i]);
		if(getfa(b[i])==getfa(b[i]+lim) || getfa(fd(a[i]))==getfa(fd(a[i])+lim)) return;
	}
	memset(vs,0,sizeof(vs));
	ss--;vs[getfa(1)]=1;
	for(i=1;i<=K;++i){
		if(a[i]==1 && !vs[getfa(b[i])]) vs[getfa(b[i])]=1,ss--;
		else if(b[i]==1 && !vs[getfa(fd(a[i]))]) vs[getfa(fd(a[i]))]=1,ss--;
	}
	ans+=fp(2,ss>>1);if(ans>=mod) ans-=mod;
}

int main(){
	int i,x;
	rd(n);rd(m);rd(K);
	for(i=1;i<=K;++i){
		rd(a[i]);rd(b[i]);rd(x);c[i]=x;
		if(a[i]==1 && b[i]==1) pr=c[i]^1;
	}
	if(pr!=0) sol(0);
	if(pr!=1) sol(1);
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值