首页 > 其他分享 >[BalticOI 2010] Mines 题解

[BalticOI 2010] Mines 题解

时间:2022-10-11 07:55:25浏览次数:87  
标签:set int 题解 Mines pmod3 inline equiv2 2010 define

你谷 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;
}

标签:set,int,题解,Mines,pmod3,inline,equiv2,2010,define
From: https://www.cnblogs.com/LaoMang-no-blog/p/16777418.html

相关文章

  • [APIO2010]巡逻
    做题时间:2022.10.10\(【题目描述】\)给定一棵\(N(N\leq10^5)\)个点的树,现在可以在这些点之间建立\(k(1\leqk\leq2)\)条边,使得从编号1的点便利一遍所有的边后返回......
  • 2022 ICPC 网络赛(II) H Fast Fourier Transform题解
    简要题意给你一棵树,你可以选若干节点为关键点,定义一个选点方案的价值为:所有路径上没有关键点的点对的距离之和。求所有选点方案的价值之和。题解一开始和队友都读错题了......
  • SCOI 萌萌哒 题解
    下决心写一道题写一篇题解。题目链接考虑暴力,直接并查集合并相同的数,和今天T1一样。考虑优化这个暴力。因为今天T1题解说要倍增,所以考虑一个长的跟ST表一样的倍增。具......
  • 洛谷 P3488 [POI2009]LYZ-Ice Skates 题解
    错解每次跑二分图匹配,时间复杂度显然爆炸。时间复杂度:我被杀手皇后摸过了正解引入Hall定理:设二分图中\(G=<V_1,V_2,E>,|V_1|\le|V_2|\),则G中存在\(V_1\)到......
  • 【题解】XXI Opencup GP of Tokyo Count Min Ratio
    有\(R\)个红球,\(B\)个蓝球和一个绿球,同色球之间无区别。将其任意排列,令\(l_R,l_B,r_R,r_B\)分别为绿球左/右边的红/蓝球数量。定义一个方案的权值为\(\max\{x\i......
  • Criss Cross OJ【R001】8月月赛I题解合集
    R0011.「T1」积木高塔Solution返回题目简要题意:给定一个矩阵,以及其每一格中完全相同立方体的高度(即个数),求:这座高塔最高点的高度。这座高塔从第\(1\)层到最高......
  • AtCoder Regular Contest 150 (ARC150) - A+B+C+D 题解
    A题意给定一个由0,1和?组成的长为\(n\)序列,其中?需要被替换为0或1,询问是否有且仅有一种?的替换方案使得序列中有\(k\)个1并且这\(k\)个1是连续的序......
  • [题解]守护者的挑战
    题目描述打开了黑魔法师Vani的大门,队员们在迷宫般的路上漫无目的地搜寻着关押applepi的监狱的所在地。突然,眼前一道亮光闪过。“我,Nizem,是黑魔法圣殿的守卫者。如果你能通......
  • Luogu P6880 [JOI 2020 Final] オリンピックバス 题解
    [P6880JOI2020Final]オリンピックバス-洛谷|计算机科学教育新生态(luogu.com.cn)两条前置知识:在图\(G\)上构造根为\(x\)的最短路树\(T\),我们删除任意一条......
  • ARC150 A+C题题解。
    如题,ARC150A题C题的解题报告。#A-Continuous1###题意:有1,0,?组成的一个序列(?可以为0/1),问恰好有且仅有k个i,且连续的情况有多少种。###分析:考虑O(n).常见为转......