SP9534 JZPLIT - Turn on the lights 题解

· · 题解

前言

题解区全是 O\left(\frac{(n+m)^3}{w}\right) 的时间复杂度错误做法,还说这题卡常。这里提供一个 O(nm) 的做法。

解法

前置推导

下文中异或运算使用 \oplus 表示,乘法(仅考虑 0,1 时等价于与运算)使用 \otimes 表示。为方便后文叙述,在这里规定若干个矩阵 / 数列 / 常量:

由题意可得一个方程(这个方程是本题的核心):

a_{i,j}=f_{i,j}\oplus R(F)_i\oplus C(F)_j

这个方程看起来很难处理,于是考虑对其进行变形。将 Ai 行的所有元素对应的方程求和,得到:

R(A)_i=R(F)_i\oplus(R(F)_i\otimes(m\bmod 2))\oplus S(F)

同理有:

C(A)_j=C(F)_j\oplus(C(F)_j\otimes(n\bmod 2))\oplus S(F)

发现这个式子与 n,m 的奇偶性有关,于是考虑分类讨论。

情况一:n,m 均为偶数

由上述推导出的方程可得:

R(A)_i=R(F)_i\oplus S(F)\\ C(A)_j=C(F)_j\oplus S(F)

于是只要确定 S(F) 即可推导出所有 R(F)C(F),进而确定所有 f_{i,j}。注意到一次操作会让 A 所有元素异或和的奇偶性变化(因为 n+m-1 为奇数),而在最后这个值会变为 0,故 S(F)=S(A),可以直接求解 f_{i,j}

情况二:n,m 其一为偶数

不妨设 n 为偶数。另一种情况是对称的。

此时的方程为:

R(A)_i=S(F)\\ C(A)_j=C(F)_j\oplus S(F) ::::info[证明] 现在只需要证明以下命题成立: - 命题一:$\bigoplus\limits_{j=1}^mf'_{i,j}=R(F')_i$; - 命题二:$\bigoplus\limits_{i=1}^nf'_{i,j}=C(F)_j$。 对于命题一,注意到初始时 $C(F)_j$ 和 $a_{i,j}$ 的值已知,意味着可以唯一确定 $R(F)_i\oplus f_{i,j}$ 的值,记为 $x_{i,j}$。分类讨论 $R(F)_i$ 的值可以推导出一个重要结论:不管 $R(F)_i$ 的值是什么,命题一成立的充要条件为 $x_{i,j}=1$ 的 $j$ 必有偶数个(重要依据是 $m$ 为奇数)。也就是说,这个命题成立与否与人为构造的 $R(F')$ 无关;又因为题目保证有解,所以命题一是天然满足的。 对于命题二,有: $$ \begin{aligned} C(F')_j&=\bigoplus\limits_{i=1}^nf'_{i,j}\\ &=\bigoplus\limits_{i=1}^n(a_{i,j}\oplus R(F')_i\oplus C(F)_j)\\ &=C(A)_j\oplus S(F)\\ &=C(F)_j \end{aligned} $$ 故命题二成立。 :::: 于是随便构造一组这样的 $R(F')$ 作为 $R(F)$ 即可。 ### 情况三:$n,m$ 均为奇数 仍然尝试沿用上面的方程,可以得到: $$ R(A)_i=S(F)\\ C(A)_j=S(F) $$ 这些方程似乎并不包含多少信息。尝试讨论 $S(F)$ 的取值: - 若 $S(F)=0$,则 $A$ 所有行、列均包含偶数个 $1$。由此注意到一种构造:直接令 $F=A$ 就是正确的!因为 $a_{i,j}=1$ 的 $(i,j)$ 会被所有满足 $f_{i',j'}=a_{i',j'}=1(i=i'\lor j=j')$ 的开关分别翻转一次,而满足该条件的开关有奇数个(对应行列各有偶数个,扣掉中间算重的一个),符合要求;$a_{i,j}=0$ 的 $(i,j)$ 会被翻转偶数次(对应行列各有偶数个,但是没有被重复计算的),也符合要求; - 若 $S(F)=1$,跟上一种情况同理,也令 $F=A$ 即可。 综上此时令 $F=A$ 即可。 ## 实现 直接根据上面的结论构造 $R(F),C(F)$,进而构造 $F$。时间复杂度 $O(nm)$。 注意,对于代码中 $n,m$ 均为奇数的情况,因为只需要满足 $F=A$,所以直接假设了 $R(F),C(F)$ 均为 $0$;而实际上的 $R(F),C(F)$ 可能为 $1$。这仅仅是为了方便实现(最终导出的 $F$ 是正确的)。 ```cpp #include<bits/stdc++.h> using namespace std; int main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n,m; cin>>n>>m; vector<string> a(n); for(auto &i:a)cin>>i; vector<int> rs(n),cs(m); // 对应文中的 R(A) / C(A) for(int i=0;i<n;i++) for(int j=0;j<m;j++) rs[i]^=a[i][j]&1,cs[j]^=a[i][j]&1; vector<int> r(n),c(m); // 对应文中的 R(F) / C(F) if(n&1){ if(!(m&1)){ int s=c[0]=cs[0]; // 使 sum C(F) 满足条件 for(int i=0;i<n;i++) r[i]=rs[i]^s; } } else{ if(m&1){ int s=r[0]=rs[0]; // 使 sum R(F) 满足条件 for(int j=0;j<m;j++) c[j]=cs[j]^s; } else{ int s=accumulate(rs.begin(),rs.end(),0,bit_xor<>()); for(int i=0;i<n;i++) r[i]=rs[i]^s; for(int j=0;j<m;j++) c[j]=cs[j]^s; } } // 分讨构造 for(int i=0;i<n;i++){ for(int j=0;j<m;j++) a[i][j]^=r[i]^c[j]; cout<<a[i]<<'\n'; } return 0; } ```