若当前模式非空,考虑包含它的 n ∗ m n*m n∗m的最小矩形,则它的四条边上都有满单元格。那么经过一次操作后,往外扩一格得到的 ( n + 2 ) ∗ ( m + 2 ) (n+2)*(m+2) (n+2)∗(m+2)的新矩形四条边上都至少有两个满单元格,改变至多一个格子的状态后,四条边上仍至少有一个满单元格。这说明一个 n ∗ m n*m n∗m的模式,经过一次操作后至少会变成 ( n + 2 ) ∗ ( m + 2 ) (n+2)*(m+2) (n+2)∗(m+2)的。
考虑倒推,若现在的模式是 n ∗ m n*m n∗m的,我们尝试找出上一次的(下面的做法说明了如果存在,这是唯一的),找不到显然就是答案。由上面的讨论,若 min ( n , m ) ≤ 2 \min(n,m)\leq 2 min(n,m)≤2,显然不存在上一次,下设 min ( n , m ) > 2 \min(n,m)>2 min(n,m)>2。
假设操作后没有额外的状态改变,找出上一次是容易的:由于当前最小矩形四条边上一次都是全空,我们可以从任意一个角开始,利用一个 ( n − 2 ) ∗ ( m − 2 ) (n-2)*(m-2) (n−2)∗(m−2)的模式,来确定上一次的模式。
现在考虑至多有一个额外状态改变的情形,我们尝试从外往内确定上一次模式,每次剥掉最外面一层。不妨设当前剥去的是最外层,那么我们可以由四条边分别得到上一次往里一层对应边的状态。注意到如果额外状态改变发生在当前剥去的某条边上,我们在确定上一次往里一层对应边的状态时会发现冲突,因此可以知道是否发生在这条边上。但是如果只使用这条边的信息,确定具体额外状态改变的位置是困难的(只能得到 m o d 3 \bmod \ 3 mod 3意义下的位置)。不过我们发现,如果我们已经知道这个额外状态改变的位置就在某条边上,那么不在这条边上的位置的信息一定是正确的,我们可以用没有额外状态改变的算法,选择一个对角开始构造剩余部分的状态,这样构造过程中不需要利用这条边。构造完后,还需要检验是否合法。
需要注意的是,上一次的模式实际大小有可能小于 ( n − 2 ) ∗ ( m − 2 ) (n-2)*(m-2) (n−2)∗(m−2),需要找出它真实大小。
时间复杂度为 O ( w 3 ) \mathcal O(w^3) O(w3)。
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
bool a[305][305],b[305][305],cur[4][305];
int n,m;
bool val[305];
const int way[9][2]={
{
0,0},{
0,1},{
0,-1},{
1,0},{
-1,0},{
1,1},{
1,-1},{
-1,1},{
-1,-1}