「APIO2011」方格染色 加权并查集

「APIO2011」方格染色

正文:

对于一张图的状态,如果我们存这张图的每一个点。那么空间复杂度是 O ( n 2 ) O(n^2) O(n2),肯定不行,更不用说时间了。

不断的画图可以发现,如果我们知道了第一行,再知道第二行第一个,我们就可以推出第二行,之后如果知道第三行第一个,就可以推出第三行……也就是我们只要知道一张图的第一列和第一行,就可以知道整张图的状态。

然后我们考虑怎么把 m p [ i ] [ j ] mp[i][j] mp[i][j]的限制推到第一行和第一列上。

m p [ 1 ] [ 1 ]   x o r   m p [ 1 ] [ j ]   x o r   m p [ i ] [ 1 ]   x o r   m p [ i ] [ j ] = ( i   m o d   2 = = 0   a n d   j   m o d   2 = = 0 ) mp[1][1]\ xor\ mp[1][j]\ xor\ mp[i][1]\ xor\ mp[i][j]=(i\ mod\ 2==0\ and\ j\ mod\ 2==0) mp[1][1] xor mp[1][j] xor mp[i][1] xor mp[i][j]=(i mod 2==0 and j mod 2==0)

解释:

因为每个2*2的矩阵的异或和都是1,故我们把在矩形X(左上角坐标为(1,1),右下角坐标为(i,j))内所有的2*2矩阵都异或起来,我们会发现,除了 m p [ 1 ] [ 1 ] , m p [ 1 ] [ j ] , m p [ i ] [ 1 ] , m p [ i ] [ j ] mp[1][1],mp[1][j],mp[i][1],mp[i][j] mp[1][1],mp[1][j],mp[i][1],mp[i][j],其他点都被异或了偶数次。于是我们只要知道这些矩形的数量即可知道这四个的点异或和。(只有当i为偶数且j为偶数时2*2的矩形数量才为奇数,异或和才为1)

由上式,可以知道 m p [ 1 ] [ j ]   x o r   m p [ 1 ] [ j ] = ( i   m o d   2 = = 0   a n d   j   m o d   2 = = 0 ) x o r   m p [ 1 ] [ 1 ]   x o r   m p [ i ] [ j ] mp[1][j]\ xor\ mp[1][j]=(i\ mod\ 2==0\ and\ j\ mod\ 2==0)xor\ mp[1][1]\ xor\ mp[i][j] mp[1][j] xor mp[1][j]=(i mod 2==0 and j mod 2==0)xor mp[1][1] xor mp[i][j]

于是,我们可以枚举 m p [ 1 ] [ 1 ] mp[1][1] mp[1][1],然后推出第一行和第一列之间的点的限制(相等或不等)。这个可以通过加权并查集实现,把所有有限制关系的点并到一起,通过存与根的关系来确定一个集合内两点间的关系。

注意判不合法的状态。

最后的方案数就是2^(集合的数量-1)。(因为 m p [ 1 ] [ 1 ] mp[1][1] mp[1][1]所在的集合的点的取值已经确定)

代码:

#include<cstdio>
#define M 1000005
#define P 1000000000
int n,m,K;
int X[M],Y[M],Col[M];
//1 Red 0 Blue
int fa[M<<1],d[M<<1];//1~n line n+1~n+m column  
int get_fa(int x){//加权并查集 
	if(x==fa[x])return x;
	int f=get_fa(fa[x]);
	d[x]^=d[fa[x]];
	fa[x]=f;
	return f;
}
int Mul(int a,int b){//快速幂 
	int res=1;
	while(b){
		if(b&1)res=1ll*res*a%P;
		a=1ll*a*a%P;
		b>>=1;
	}
	return res;
}
int Solve(int op){
	for(int i=1;i<=n+m;i++)fa[i]=i,d[i]=0;
	fa[n+1]=1;//左上角 (1,1)既属于第一行也属于第一列 
	for(int i=1;i<=K;i++){
		if(X[i]==1&&Y[i]==1)continue;//1,1就不用判了 
		int tmp=(X[i]%2==0&&Y[i]%2==0)^Col[i]^op;
		int a=get_fa(X[i]),b=get_fa(Y[i]+n);
		int res=d[X[i]]^d[Y[i]+n]^tmp;
		if(a==b&&res)return 0;//Col[X[i]]^Col[Y[i]+n]!=tmp
		//printf("@%d %d %d\n",X[i],Y[i]+n,tmp);
		fa[a]=b;
		d[a]^=res;
	}
	int cnt=0;
	for(int i=1;i<=n+m;i++)if(fa[i]==i)cnt++;
	return Mul(2,cnt-1);//mp[1][1]已经确定 
}
int main(){
	scanf("%d%d%d",&n,&m,&K);
	int flag=-1;
	for(int i=1;i<=K;i++)scanf("%d%d%d",&X[i],&Y[i],&Col[i]);
	for(int i=1;i<=K;i++)if(X[i]==1&&Y[i]==1)flag=Col[i];//如果1,1的值已经确定 
	if(flag==0)printf("%d\n",Solve(0));
	else if(flag==1)printf("%d\n",Solve(1));
	else printf("%d\n",(Solve(0)+Solve(1))%P);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值