[BalticOI 2010] Mines 题解

你谷 link

loj link

提供一种时间复杂度正确的高妙做法。

首先题目有一条隐藏条件是保证 \(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;
}
posted @ 2022-10-11 07:47  老莽莽穿一切  阅读(141)  评论(1编辑  收藏  举报