• [BZOJ2303][Apio2011]方格染色


    [BZOJ2303][Apio2011]方格染色

    试题描述

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

    输入

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

    输出

    输出一个整数,表示可能的染色方案数目 W 模 10^9得到的值。(也就是说,如果 W大于等于10^9,则输出 W被10^9除所得的余数)。 

    输入示例

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

    输出示例

    8

    数据规模及约定

    对于所有的测试数据,2 ≤ n, m ≤ 10^6,0 ≤ k ≤ 10^6,1 ≤ xi ≤ n,1 ≤ yi ≤ m。

    题解

    这种带上某种限制再染色的题目一般都是先确定第一行,再依次确定2~n行,比如这题。

    假设先不考虑事先涂上的格子,红色用1表示,蓝色用0表示。我们可以枚举第一行,接下来第 i 行可以是第 i-1 行的全部奇数位取异或值,其余不变;或是第 i-1 行的全部偶数位取异或值,其余不变。于是每向下处理一行答案数扩大1倍,即 方案数 = 第一行方案数 × 2n-1

    现在考虑事先涂上的格子,不考虑无解的情况,不难发现同一行若是有两列(第 a 列和第 b 列)都有涂好颜色的格子,那么第一行的第 a 列于第 b 列的颜色就会有一个制约关系,所以把同行的两列合并成一个连通分量,最后看不含第一行位置的连通块的个数,设这个个数为 t,则 第一行方案数 = 2t. 注意,这时 方案数 = 第一行方案数 × 2没有涂颜色的行数

    再特判一下无解的情况,之前提到一个制约关系,现在我们就好好研究一下这个制约关系。(设某一行第 a 列和第 b 列数的相等关系为p,p=1时不等,p=0时相等;第一行第 a 列和第 b 列数的相等关系为q,q=1时不等,q=0时相等)

    有如下关系:

    1.) a 与 b 奇偶性相同时,q = p;

    2.) a 与 b 奇偶性不同时,q = p Xor (行数 - 1)。

    用并查集维护2m个点,表示第一行每个数(Ai)等于 1 或 0 的条件,每个连通块表示其中所有条件全部成立。例如,当第 a 列和第 b 列数不同时,把(Aa = 1)与(Ab = 0)加入同一个连通块,同时把(Aa = 0)与(Ab = 1)也加入同一个连通块。最后再看是否有(Ai = 0)与(Ai = 1)在同一个连通块中就行了。

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <vector>
    #include <queue>
    #include <cstdlib>
    #include <ctime>
    using namespace std;
    
    const int BufferSize = 1 << 16;
    char buffer[BufferSize], *Head, *tail;
    inline char Getchar() {
    	if(Head == tail) {
    		int l = fread(buffer, 1, BufferSize, stdin);
    		tail = (Head = buffer) + l;
    	}
    	return *Head++;
    }
    int read() {
    	int x = 0, f = 1; char c = Getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    	return x * f;
    }
    
    #define maxn 1000010
    #define MOD 1000000000
    #define LL long long
    int n, m, k, last[maxn];
    
    int fa[maxn], f2[maxn<<1];
    bool val[maxn], line[maxn];
    int findset(int x) { return x == fa[x] ? x : fa[x] = findset(fa[x]); }
    int findset2(int x) { return x == f2[x] ? x : f2[x] = findset2(f2[x]); }
    
    struct Point { int x, y, c; } ps[maxn];
    
    int Pow(int a, int b) {
    	if(!b) return 1;
    	b--; int t = a;
    	while(b) {
    		if(b & 1) a = ((LL)a * t) % MOD;
    		b >>= 1; t = ((LL)t * t) % MOD;
    	}
    	return a;
    }
    
    bool add(int a, int b, bool c1, bool c2, int x) {
    	if((a & 1) ^ (b & 1)) c2 ^= (x & 1);
    	int u = findset2((a << 1) | c1), v = findset2((b << 1) | c2);
    	if(u != v) f2[v] = u;
    	c1 ^= 1; c2 ^= 1;
    	u = findset2((a << 1) | c1); v = findset2((b << 1) | c2);
    	if(u != v) f2[v] = u;
    	if(findset2(a << 1) == findset2((a << 1) | 1) || findset2(b << 1) == findset2((b << 1) | 1)) return 1;
    	return 0;
    }
    
    int main() {
    	n = read(); m = read(); k = read();
    	for(int i = 1; i <= k; i++) {
    		ps[i].x = read(); ps[i].y = read(); ps[i].c = read();
    		if(ps[i].x == 1) val[ps[i].y] = 1;
    		line[ps[i].x] = 1;
    	}
    	
    	int cnt = m;
    	for(int i = 1; i <= m; i++) fa[i] = i;
    	for(int i = 1; i <= (m << 1) + 1; i++) f2[i] = i;
    	for(int i = 1; i <= k; i++) {
    		if(last[ps[i].x]) {
    			int t = last[ps[i].x];
    			int a = findset(ps[i].y), b = findset(ps[t].y);
    			if(a != b) {
    				fa[b] = a;
    				val[a] |= val[b]; val[b] = 0;
    				cnt--;
    			}
    			if(add(ps[i].y, ps[t].y, ps[i].c, ps[t].c, ps[i].x + 1)) {
    				puts("0");
    				return 0;
    			}
    		}
    		last[ps[i].x] = i;
    	}
    	for(int i = 1; i <= m; i++) if(val[i]) cnt--;
    	int tmp = 0;
    	for(int i = 2; i <= n; i++) if(!line[i]) tmp++;
    	int ans = Pow(2, cnt + tmp);
    	
    	printf("%d
    ", ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    [Django]Windows下Django配置Apache示范设置
    《职场》笔记20061119
    Python Django还是RoR,这是一个问题
    收集证据:fsjoy.com的流氓推广和幕后流氓主子[updated]
    爱尔兰网友邀请我对Dublin交通监视器进行手机端开发
    {基于Applet的J2ME模拟器}和{microemulator}[J2ME推荐]
    中国移动IM飞信0802上线新版本 试用手记
    [AsyncHandle]什么引发了ObjectDisposedException?
    百度的“搜索背后的人”的战略
    [Python]检查你的站点的人气
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/5440147.html
Copyright © 2020-2023  润新知