题目链接
题目大意
Sam 和他的妹妹 Sara 有一个包含 n × m n \times m n×m 个方格的表格。他们想要将其中的每个方格都染成红色或蓝色。出于个人喜好,他们想要表格中每个 2 × 2 2 \times 2 2×2 的方形区域都包含奇数个( 1 个或 3 个)红色方格。例如,下面是一个合法的表格染色方案(R 代表红色,B 代表蓝色):
B B R B R
R B B B B
R R B R B
可是昨天晚上,有人已经给表格中的一些方格染上了颜色!现在 Sam 和 Sara 非常生气。不过,他们想要知道是否可能给剩下的方格染上颜色,使得整个表格依然满足他们的要求。如果可能的话,满足他们要求的染色方案数有多少呢?
输入描述
输入的第一行包含三个整数 n , m , k n,m,k n,m,k 分别代表表格的行数,列数和已被染色的方格数目。
之后的 k k k 行描述已被染色的方格。其中第 i i i 行包含三个整数 x i , y i , c i x_i,y_i,c_i xi,yi,ci ,分别代表第 i i i 个已被染色的方格的行编号、列编号和颜色。 c i c_i ci为 1 表示方格被染成红色, c i c_i ci 为 0 表示方格被染成蓝色。
其中, 2 ⩽ n , m ⩽ 1 0 5 2 \leqslant n,m \leqslant 10^5 2⩽n,m⩽105, 0 ⩽ k ⩽ 1 0 5 0 \leqslant k \leqslant 10^5 0⩽k⩽105, 1 ⩽ x i ⩽ n 1 \leqslant x_i \leqslant n 1⩽xi⩽n, 1 ⩽ y i ⩽ m 1 \leqslant y_i \leqslant m 1⩽yi⩽m, ∀ c i ∈ 0 , 1 \forall c_i \in {0,1} ∀ci∈0,1
输出描述
输出一个整数,表示可能的染色方案数 w w w 对于 1 0 9 10^9 109 取模后得到的值。
输入输出样例
输入
3 4 3
2 2 1
1 2 0
2 3 1
输出
8
运行限制
- 最大运行时间:1s
- 最大运行内存: 128M
解题思路
异或运算+加权并查集
数据集量分析
2
⩽
n
,
m
⩽
1
0
5
2 \leqslant n,m \leqslant 10^5
2⩽n,m⩽105,
0
⩽
k
⩽
1
0
5
0 \leqslant k \leqslant 10^5
0⩽k⩽105,
1
⩽
x
i
⩽
n
1 \leqslant x_i \leqslant n
1⩽xi⩽n,
1
⩽
y
i
⩽
m
1 \leqslant y_i \leqslant m
1⩽yi⩽m,
∀
c
i
∈
0
,
1
\forall c_i \in {0,1}
∀ci∈0,1
n
,
m
n,m
n,m 的取值范围发现,如果利用矩阵存储整个表格,那么其所需内存空间将超出 128MB ,且时间复杂度也不允许。因此不能使用二维矩阵去表达这个表格。
关键思路
假设知道表格中第一行和第一列的染色情况,根据 2 × 2 2\times 2 2×2 区域只能有奇数个 c i = 1 c_{i}=1 ci=1 的要求,推导推导出整个表格的合法状态,因此,只需记录表格的第一行和第一列,就相当于记录了表格的整个状态。
异或运算:由于表格中 1 代表红色, 0 代表蓝色,从异或运算的角度出发。根据题意,可以推出满足要求的 2 × 2 2\times2 2×2 区域的异或和必为 1 。
1、设定此表格名称为 map ,下标从 1 开始。
2、假如当前已知map
[
i
]
[
j
]
[i][j]
[i][j] 被涂上颜色。然后在
i
,
j
i,j
i,j 范围内进行
2
×
2
2\times 2
2×2 异或和运算,发现除了
m
a
p
[
1
]
[
1
]
map[1][1]
map[1][1],map
[
i
]
[
1
]
[i][1]
[i][1],map
[
1
]
[
j
]
[1][j]
[1][j],map
[
i
]
[
j
]
[i][j]
[i][j] 四个“矩形端点”以外,其余的方格都参与了 2 次异或运算相抵消为 0 ,而唯独四个端点只参与了 1 次异或运算。因此,
i
,
j
i,j
i,j 范围内所有点的异或和等价于这 4 个端点的异或和。
i
,
j
i,j
i,j 范围内所有点所有点的异或和:
通过计算表格能容纳的
2
×
2
2\times2
2×2 矩形框数量来求解
(解释:每个
2
×
2
2\times2
2×2 矩形框的异或和都是 1 ,因此如果有偶数个矩形框,那么表格中点的异或和就是 0 ,否则,异或和是 1 。)
推导可以得出,仅当
i
,
j
i,j
i,j 均为偶数时,矩形框的个数
(
i
−
1
)
×
(
j
−
1
)
(i-1)\times(j-1)
(i−1)×(j−1) 才为奇数,此时异或和为 1 。
由 4 端点异或和和表格异或和的等价关系和上述推导,可以得出如下式子:
m
a
p
[
1
]
[
1
]
x
o
r
m
a
p
[
1
]
[
j
]
x
o
r
m
a
p
[
i
]
[
1
]
x
o
r
m
a
p
[
i
]
[
j
]
=
(
i
%
2
=
0
a
n
d
j
%
2
=
0
)
{\rm map}[1][1]\; xor \;{\rm map}[1][j]\; xor\;{\rm map}[i][1]\;xor\;{\rm map}[i][j]\;=(i\%2=0\; and\; j\%2=0)
map[1][1]xormap[1][j]xormap[i][1]xormap[i][j]=(i%2=0andj%2=0)
即等价于
m
a
p
[
1
]
[
j
]
x
o
r
m
a
p
[
i
]
[
1
]
=
(
i
%
2
=
0
a
n
d
j
%
2
=
0
)
x
o
r
m
a
p
[
1
]
[
1
]
x
o
r
m
a
p
[
i
]
[
j
]
{\rm map}[1][j]\; xor \;{\rm map}[i][1]=(i\%2=0\; and\; j\%2=0)\;xor\;{\rm map}[1][1]\; xor\;{\rm map}[i][j]
map[1][j]xormap[i][1]=(i%2=0andj%2=0)xormap[1][1]xormap[i][j]
因此可以枚举 map [ 1 ] [ 1 ] [1][1] [1][1],通过加权并查集把所有有限制关系的点并到一起,通过存与根的关系来确定一个集合内两点间的关系。这样即可推出第一行和第一列之间的点的限制(相等或不等),要注意判不合法的状态。
最后的方案数就是 2 s e t _ n u m − 1 2^{set\_num-1} 2set_num−1。
reference:https://blog.csdn.net/qq_35320178/article/details/99672268
AC Code
Python3
# -*- coding: utf-8 -*-
# @Author : BYW-yuwei
# @Software: python3.8.6
M=int(1e6+5)
MOD=int(1e9) #取余数的数
x_idx=[0] ; y_idx=[0] ; color=[0]
father=[0]*M; d=[0]*M
def init(lim:int): #加权并查集初始化模板init
for i in range(1,lim+1):
father[i]=i ; d[i]=0
def get_father(x:int): #并查集组合的合并,权重数组保存异或值
if x==father[x]:return x
f=get_father(father[x])
d[x]^=d[father[x]]
father[x]=f
return f
def mul(a:int,b:int): #快速幂运算模板mul(x,y)-->pow(x,y)
ans=1
while b:
if b&1:ans=(ans*a)%MOD
a=(a*a)%MOD
b>>=1
return ans
def solve(op:int):
init(n+m)
father[n+1]=1
global k
for i in range(1,k+1):
if x_idx[i]==1 and y_idx[i]==1:continue #(1,1)点无需再次判断
temp=((x_idx[i]%2==0) and (y_idx[i]%2==0))^color[i]^op
a=get_father(x_idx[i]) ; b=get_father(y_idx[i]+n)
res=d[x_idx[i]]^d[y_idx[i]+n]^temp
if a==b and res:return 0
father[a]=b
d[a]^=res
cnt=0
for i in range(1,n+m+1):
if father[i]==i:cnt+=1
return mul(2,cnt-1)
if __name__=='__main__':
n,m,k=map(int,input().split())
flag=-1
for i in range(k):
idx,idy,col=map(int,input().split())
x_idx.append(idx);y_idx.append(idy);color.append(col)
for i in range(1,k+1):
if (x_idx[i]==1) and (y_idx[i]==1):flag=color[i]
if flag==0:
print(solve(0))
elif flag==1:
print(solve(1))
else:
print((solve(0)+solve(1))%MOD)