zoukankan      html  css  js  c++  java
  • 【洛谷P3631】方格染色

    题目

    题目链接:https://www.luogu.com.cn/problem/P3631
    Sam 和他的妹妹 Sara 有一个包含 (n imes m) 个方格的表格。他们想要将其中的每个方格都染成红色或蓝色。出于个人喜好,他们想要表格中每个 (2 imes 2) 的方形区域都包含奇数个( (1) 个或 (3) 个)红色方格。例如,下面是一个合法的表格染色方案(R 代表红色,B 代表蓝色):

    B B R B R
    R B B B B
    R R B R B
    

    可是昨天晚上,有人已经给表格中的一些方格染上了颜色!现在 Sam 和 Sara 非常生气。不过,他们想要知道是否可能给剩下的方格染上颜色,使得整个表格依然满足他们的要求。如果可能的话,满足他们要求的染色方案数有多少呢?
    (n,m,kleq 10^5)

    思路

    手画了前面一些情况猜了一个结论过了 (70\%) 的点,死调调不出来,看题解发现是结论猜错了一点点 /fad。瞎忙活一个小时。
    把红色看做 (1),蓝色看做 (0),那么等价于任意 (2 imes 2) 子矩阵的异或和为 (1)
    显然如果我们确定了第一行第一列的所有数字,那么就可以推出整个矩阵。这启发我们寻找每一个点与第一行第一列数字之间的关系。
    画一下不难发现,对于点 ((x,y)),如果 (x,y) 都是偶数,那么 (a_{x,y} ext{ xor }a_{x,1} ext{ xor }a_{1,y} ext{ xor }a_{1,1}=1),否则 (a_{x,y} ext{ xor }a_{x,1} ext{ xor }a_{1,y} ext{ xor }a_{1,1}=0)
    考虑枚举 (a_{1,1}) 的值,然后其实就相当于给了 (a_{x,1} ext{ xor }a_{1,y}) 的若干限制。
    考虑扩展域并查集,把每一个格子拆成 (0)(1),然后对于一个限制直接连边即可。
    为了处理 (x=1)(y=1) 的情况,我们再记 (vis_x) 表示 (x) 所在集合是否可取,一开始对于限制 ((1,y,z)),就让代表 ((1,y,x ext{ xor } 1))(vis) 设为 (1),其他同理。
    最后统计答案的时候先枚举所有点判断拆出来的两个点是否在同一连通块,如果不存在这样的点,那么就枚举所有连通块,利用它的 (vis) 和它所对应另一个连通块的 (vis) 计算对答案的贡献即可。
    时间复杂度 (O((n+m)alpha(n)))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=100010,MOD=1e9;
    int n,m,t,ans,res,X[N],Y[N],Z[N],father[N*4],vis[N*4];
    
    int find(int x)
    {
    	return x==father[x]?x:father[x]=find(father[x]);
    }
    
    void merge(int x,int y)
    {
    	x=find(x); y=find(y);
    	vis[x]|=vis[y]; father[y]=x;
    }
    
    int main()
    {
    	scanf("%d%d%d",&n,&m,&t);
    	for (int i=1;i<=t;i++)
    	{
    		scanf("%d%d%d",&X[i],&Y[i],&Z[i]);
    		Y[i]+=n;
    	}
    	t++; X[t]=1; Y[t]=n+1;
    	for (Z[t]=0;Z[t]<=1;Z[t]++)
    	{
    		res=1;
    		for (int i=1;i<N*4;i++)
    			father[i]=i,vis[i]=0;
    		merge(1,n+1);
    		for (int i=1;i<=t;i++)
    		{
    			if (Y[i]==n+1) vis[X[i]+(!Z[i])*(n+m)]=1;
    			if (X[i]==1) vis[Y[i]+(!Z[i])*(n+m)]=1;
    		}
    		for (int i=1;i<=t;i++)
    		{
    			int k=(X[i]&1)|(Y[i]&1);
    			if (X[i]!=1 && Y[i]!=n+1 && !(Z[i]^Z[t]^k))
    			{
    				merge(X[i],Y[i]+n+m);
    				merge(X[i]+n+m,Y[i]);
    			}
    			else if (X[i]!=1 && Y[i]!=n+1)
    			{
    				merge(X[i],Y[i]);
    				merge(X[i]+n+m,Y[i]+n+m);
    			}
    		}
    		bool flag=1;
    		for (int i=1;i<=n+m;i++)
    			if (find(i)==find(i+n+m)) { flag=0; break; }
    		if (!flag) continue;
    		for (int i=1;i<=n+m;i++)
    			if (find(i)==i)
    			{
    				if (!vis[i] && !vis[find(i+n+m)]) res=res*2%MOD;
    				if (vis[i] && vis[find(i+n+m)]) res=0;
    			}
    		ans+=res;
    	}
    	printf("%d",ans%MOD);
    	return 0;
    }
    
  • 相关阅读:
    三元表达式 列表和字典推导式 函数对象 名称空间 作用域 global和nonlocal 函数装饰器 枚举对象
    函数参数 打散机制 字符串比较 返回值
    函数简介
    三种字符串的介绍 文件的读写
    字符编码
    数据类型及其常用方法 数据类型转换 可变与不可变 值拷贝与深浅拷贝
    流程控制 while和for循环
    变量命名规范 常量 输入和输出 注释 数据类型 运算符 逻辑运算符
    语言分类 编译型和解释型语言分析 环境变量 代码执行的方式 pip介绍 变量
    Python django tests
  • 原文地址:https://www.cnblogs.com/stoorz/p/14584645.html
Copyright © 2011-2022 走看看