提供一种时间复杂度正确的高妙做法。
首先题目有一条隐藏条件是保证 \(n\not\equiv2\pmod3\) 或 \(m\not\equiv2\pmod3\),需要通过观察数据得到。
我们钦定 \(n\not\equiv2\pmod3\),如果不满足则沿主对角线翻转使其满足。
接下来我们令 \(a_{i,j}\) 表示原给定数字表格上第 \(i\) 行第 \(j\) 列的值,\((i,j)\) 表示第 \(i\) 行第 \(j\) 列是否有雷。
首先考虑一种特殊情况,\(a_{x,y}+a_{x+1,y+1}-a_{x,y+1}-a_{x+1,y}=(x-1,y-1)+(x+2,y+2)-(x-1,y+2)-(x+2,y-1)\),即对于一个 \(2\times2\) 的方格,若我们已知其三个角,我们可以直接求得另外一个角。
思路非常巧妙,首先确定第 \(3\) 行。
利用前面的特殊情况可以递推求出所有 \((3,3k)\),接下来求别的,需要对 \(m\) 分类讨论。
若 \(m\not\equiv2\pmod3\),我们将整个图水平翻转,再做一遍,则第三行任意三个相邻的位置只有一个未知,可以直接求。
若 \(m\equiv2\pmod3\),此时我们将第三行中所有未知的位置取出来排成一列,任意两个相邻位置的和都是已知的,若有 \(2\) 或 \(0\),两个就都确定了,若全都是 \(1\),则我们发现任意两个相邻的位置里只要有一个雷就好了,所以直接任意确定一组没有关系。
以此类推,当第 \(3\) 行已知后,将第 \(3\) 行对全图的影响消去,第 \(6\) 行的情况就等价于原来的第三行,以此类推求得所有第 \(3k\) 行。
因为保证了 \(n\not\equiv2\pmod3\),竖直翻转后重新求一遍则所有未知行两两之间隔了两行,不会互相影响,即可以独立求。
求一个 \(1\times m\) 的矩阵非常简单,直接和上面求第 \(3\) 行类似做就好了。
最佳实现时间复杂度是 \(\mathcal O\left(nm\right)\)。
实现上因为全图翻转是 \(\mathcal O\left(nm\right)\) 的,所以上面讲到做第 \(3\) 行时翻转,实际上不能做一次翻一次,时间复杂度会退化,因为上面讲到特殊情况中有影响的实际上只有四个顶点,那么只要全部一起做就可以只反翻转一次。
细节较多,一个比较好的实现是每确定一个雷就将它在图中的影响消去,写起来会相对简单。
c++ 代码
#include<bits/stdc++.h>
using namespace std;
#define Reimu inline void // 灵梦赛高
#define Marisa inline int // 魔理沙赛高
#define Sanae inline bool // 早苗赛高
#define Reisen inline LL // 铃仙赛高
typedef long long LL;
typedef unsigned long long ULL;
typedef __int128 Suika;
inline ostream &operator<<(ostream &cout, Suika x) {
static const LL LIM = 1e18;
return x < LIM ? cout << LL(x) : cout << LL(x / LIM) << setw(18) << setfill('0') << LL(x % LIM);
}
typedef pair<int, int> Pii;
typedef tuple<int, int, int> Tiii;
#define fi first
#define se second
#define all(vec) vec.begin(), vec.end()
#define TY(type) const type&
template<typename Ty>
Reimu clear(Ty &x) { Ty().swap(x); }
const int N = 610;
int n, m, revFlg;
int fr[3], fc[3];
char s[N];
int a[N][N], b[N][N];
Reimu swp(int i1, int j1, int i2, int j2) { swap(a[i1][j1], a[i2][j2]); swap(b[i1][j1], b[i2][j2]); }
Reimu reverse1() {
int mx = max(n, m);
swap(n, m);
for (int i = 1; i <= mx; ++i) {
for (int j = 1; j < i; ++j) {
swp(i, j, j, i);
}
}
}
Reimu reverse2() {
for (int i = 1; i <= n >> 1; ++i) {
for (int j = 1; j <= m; ++j) {
swp(i, j, n - i + 1, j);
}
}
}
Reimu reverse3() {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m >> 1; ++j) {
swp(i, j, i, m - j + 1);
}
}
}
#define safe(i, j) ((i) > 0 && (i) <= n && (j) > 0 && (j) <= m)
#define set set_
Reimu set(int x, int y, int o) {
if (!o) return;
b[x][y] = 1;
for (int i: {x - 1, x, x + 1}) for (int j: {y - 1, y, y + 1}) if (safe(i, j)) --a[i][j];
}
Reimu solve1() {
auto calc1 = [&](int i) { for (int j = 3; j <= m; j += 3) set(i, j, a[i - 2][j - 2] + a[i - 1][j - 1] - a[i - 2][j - 1] - a[i - 1][j - 2]); };
for (int i = 3; i <= n; i += 3) calc1(i);
if (fc[0]) {
reverse3();
for (int i = 3; i <= n; i += 3) calc1(i);
reverse3();
for (int i = 3; i <= n; i += 3) {
for (int j = 1; j <= m; ++j) if (!fc[j % 3]) {
set(i, j, a[i - 1][j] - a[i - 2][j]);
}
}
return;
}
auto calc2 = [&](int i) {
for (int j = 0; j <= m; j += 3) {
if (j) do {
int t = a[i - 1][j] - a[i - 2][j];
if (t & 1) continue;
t >>= 1; set(i, j - 1, t); set(i, j + 1, t);
for (int k = j - 2; k > 0; --k) if (k % 3) set(i, k, a[i - 1][k + 1] - a[i - 2][k + 1]);
for (int k = j + 2; k <= m; ++k) if (k % 3) set(i, k, a[i - 1][k - 1] - a[i - 2][k - 1]);
return;
} while (false);
int t = a[i - 1][j + 1] - a[i - 2][j + 1];
if (t & 1) continue;
t >>= 1; set(i, j + 1, t); set(i, j + 2, t);
for (int k = j - 1; k > 0; --k) if (k % 3) set(i, k, a[i - 1][k + 1] - a[i - 2][k + 1]);
for (int k = j + 4; k <= m; ++k) if (k % 3) set(i, k, a[i - 1][k - 1] - a[i - 2][k - 1]);
return;
}
for (int j = 0; j <= m; j += 3) set(i, j + 1, 1);
};
for (int i = 3; i <= n; i += 3) calc2(i);
}
Reimu solve2() {
auto calc1 = [&](int i) { for (int j = 3; j <= m; j += 3) set(i, j, a[i][j - 1] - a[i][j - 2]); };
for (int i = 1; i <= n; ++i) if (!fr[i % 3]) calc1(i);
if (fc[0]) {
reverse3();
for (int i = 1; i <= n; ++i) if (!fr[i % 3]) calc1(i);
reverse3();
for (int i = 1; i <= n; ++i) if (!fr[i % 3]) {
for (int j = 1; j <= m; ++j) if (!fc[j % 3]) {
set(i, j, a[i][j]);
}
}
return;
}
auto calc2 = [&](int i) {
for (int j = 0; j <= m; j += 3) {
if (j) do {
int t = a[i][j];
if (t & 1) continue;
t >>= 1; set(i, j - 1, t); set(i, j + 1, t);
for (int k = j - 2; k > 0; --k) if (k % 3) set(i, k, a[i][k + 1]);
for (int k = j + 2; k <= m; ++k) if (k % 3) set(i, k, a[i][k - 1]);
return;
} while (false);
int t = a[i][j + 1];
if (t & 1) continue;
t >>= 1; set(i, j + 1, t); set(i, j + 2, t);
for (int k = j - 1; k > 0; --k) if (k % 3) set(i, k, a[i][k + 1]);
for (int k = j + 4; k <= m; ++k) if (k % 3) set(i, k, a[i][k - 1]);
return;
}
for (int j = 0; j <= m; j += 3) set(i, j + 1, 1);
};
for (int i = 1; i <= n; ++i) if (!fr[i % 3]) calc2(i);
}
Reimu solve() {
solve1(); reverse2();
solve1(); reverse2();
solve2();
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> s + 1;
for (int j = 1; j <= m; ++j) a[i][j] = s[j] & 15;
}
if (n % 3 == 2) revFlg = 1, reverse1();
fr[0] = fc[0] = 1; fr[(n + 1) % 3] ^= 1; fc[(m + 1) % 3] ^= 1;
solve();
if (revFlg) reverse1();
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) cout << ".X"[b[i][j]];
cout << '\n';
}
return 0;
}