[bzoj2303][Apio2011]方格染色

Description

Sam和他的妹妹Sara有一个包含n×m个方格的表格。她们想要将其的每个方格都染成红色或蓝色。
出于个人喜好,他们想要表格中每个2×2的方形区域都包含奇数个(1 个或 3 个)红色方格。
可是昨天晚上,有人已经给表格中的一些方格染上了颜色!现在Sam和Sara非常生气。不过,他们想要知道是否可能给剩下的方格染上颜色,使得整个表格仍然满足她们的要求。如果可能的话,满足他们要求的染色方案数有多少呢?

Input

输入的第一行包含三个整数n,m和k,分别代表表格的行数、列数和已被染色的方格数目。
之后的k行描述已被染色的方格。其中第 i 行包含三个整数 \(x_i,y_i,c_i\),分别代表第 i 个已被染色的方格的行编号、列编号和颜色。\(c_i\) 为 1 表示方格被染成红色,\(c_i\) 为 0 表示方格被染成蓝色。

Output

输出一个整数,表示可能的染色方案数目 W 模 \(10^9\)得到的值。

Sample Input

3 4 3
2 2 1
1 2 0
2 3 1

Sample Output

8

HINT
\(2\leq n,m\leq10^6,0\leq k\leq10^6,1\leq x_i\leq n,1\leq y_i\leq m\).

Solution
设红色为1,蓝色为0,问题约束可以转化成要求每个2×2的方形区域的异或和为1.

如果第一行已经确定,那么后面的每一行要么是上一行的奇数列 xor 1,要么是上一行的偶数列 xor 1.那么方案数为 \(\large{2^{空行数}}\).

现在需要确认是否存在这样的合法的第一行,也就是确认每两个格子间的颜色异或关系是否矛盾.

对于每个同在第i行的已染色的格子\(x,y\),如果它们列数同奇偶,则它们在第一行的关系为\(c_x\;xor\;c_y\); 如果它们列数不同奇偶,则它到第一行一共做了(i-1)变换,它们在第一行的关系为\(c_x\;xor\;c_y\;xor\;(i-1)\).
然后用并查集维护列数,合并同行的已染色的格子,记录其与父亲的异或关系(方便O(1)求出同连通块的格子间的异或关系,判合法性).第一行的方案数即为 \(\large{2^{第一行未染色的连通块数}}\).

#define N 1000005
#define M 1000000000
typedef long long ll;
struct grid{
	int x,y,c;
	bool friend operator <(grid a,grid b){
		if(a.x!=b.x) return a.x<b.x;
		return a.y<b.y;
	}
}a[N];
int c[N]/*col[1][i]^col[1][f[i]]*/,f[N],n,m,k,tot;
bool b[N],v[N];
inline int gf(int k){
	if(f[k]==k) return k;
	int fa=gf(f[k]);
	c[k]^=c[f[k]];
	return f[k]=fa;
}
inline bool chk(int x,int y,int t){
	int p=gf(x),q=gf(y);
	if(p^q){
		if(b[p]||b[q]) b[p]=b[q]=true;
		f[p]=q;c[p]=c[x]^c[y]^t;
		return true;
	}
	else return (c[x]^c[y])==t;
}
inline int po(int x,int k){
	int ret=1;
	while(k){
		if(k&1) ret=1ll*ret*x%M;
		x=1ll*x*x%M;k>>=1;
	}
	return ret;
}
inline void Aireen(){
	n=read();m=read();k=read();
	for(int i=1;i<=k;++i){
		a[i]=(grid){read(),read(),read()};
		if(a[i].x==1) b[a[i].y]=true;
		v[a[i].x]=true;
	}
	sort(a+1,a+1+k);
	for(int i=1;i<=m;++i) f[i]=i;
	for(int i=2,x,y,t;i<=k;++i){
		while(a[i].x==a[i-1].x){
			x=a[i].y;y=a[i-1].y;
			t=a[i].c^a[i-1].c;
			if((x&1)^(y&1)) t^=((a[i].x-1)&1);
			if(!chk(x,y,t)){
				puts("0");return;
			}
			++i;
		}
	}
	for(int i=1;i<=m;++i)
		if(gf(i)==i&&!b[i]) ++tot;
	for(int i=2;i<=n;++i)
		if(!v[i]) ++tot;
	printf("%d\n",po(2,tot));
}

2017-05-03 17:56:48

posted @ 2021-11-25 14:45  Aireen_Ye  阅读(91)  评论(0编辑  收藏  举报
底部 顶部 留言板 归档 标签
Der Erfolg kommt nicht zu dir, du musst auf den Erfolg zugehen.