题解:P16724 [GKS 2019 #A] Parcels
注意到题目中出现了「最大值的最小值」这样的表述,考虑二分答案
然而这并不好判定。考虑找出离每个办公室曼哈顿距离不超过
为了获取每个格子的覆盖范围,可以想到二维差分,然而,处理旋转了
具体地,记每个格子原有的坐标为
这样可以将每个格子的横纵坐标映射在
使用差分得到现有的覆盖范围可以覆盖哪些格子之后,我们找出没有被覆盖的格子的横、纵坐标的最小值
那么它就可以作为一个新办公室的位置。如果不能找到这样的坐标,就说明此时的
#include <bits/stdc++.h>
using ll = long long;
using ld = long double;
using ull = unsigned long long;
const int INF = 0x3f3f3f3f, _INF = 0xc0c0c0c0;
const ll LLINF = 0x3f3f3f3f3f3f3f3f, _LLINF = 0xc0c0c0c0c0c0c0c0;
namespace IO {
// 略
} // namespace IO
using IO::err; using IO::read; using IO::write;
const int N = 255;
int T, R, C;
int d[N * 2][N * 2];
int p[N * N][2], cnt;
int Case;
bool check(int k) {
memset(d, 0, sizeof(d));
for (int i = 1; i <= cnt; i++) {
int x = p[i][0], y = p[i][1];
d[std::max(x - k, 1)][std::max(y - k, 1)]++;
d[std::max(x - k, 1)][std::min(y + k + 1, 500)]--;
d[std::min(x + k + 1, 500)][std::max(y - k, 1)]--;
d[std::min(x + k + 1, 500)][std::min(y + k + 1, 500)]++;
}
for (int i = 1; i <= 499; i++) {
for (int j = 1; j <= 499; j++) {
d[i][j] += d[i-1][j] + d[i][j-1] - d[i-1][j-1];
}
}
int minx = INF, maxx = 0, miny = INF, maxy = 0;
for(int i = 1; i <= 499; i++) {
bool ok = false;
for (int j = 1; j <= 499; j++) {
// 先转回原坐标系检验。
if (((i + j) & 1) == 0) continue;
int mx = (i + j - 249) >> 1, my = (j - i + 251) >> 1;
if (mx <= 0 || mx > R || my <= 0 || my > C) continue;
if (d[i][j] == 0) {
minx = std::min(minx, i);
miny = std::min(miny, j);
maxx = std::max(maxx, i);
maxy = std::max(maxy, j);
}
}
}
if (maxx - minx > 2 * k || maxy - miny > 2 * k) {
return false;
}
for (int i = 1; i <= R; i++) {
for (int j = 1; j <= C; j++) {
int x = i - j + 250, y = i + j - 1;
if (x >= maxx - k && x <= minx + k && y >= maxy - k && y <= miny + k) {
return true;
}
}
}
return false;
}
void solve() {
Case++;
cnt = 0;
read(R, C);
for (int i = 1; i <= R; i++) {
for (int j = 1; j <= C; j++) {
char ch;
read(ch);
if (ch == '1') {
p[++cnt][0] = i - j + 250; p[cnt][1] = i + j - 1;
}
}
}
int l = 0, r = 500;
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid)) {
r = mid;
} else l = mid + 1;
}
write("Case #", Case, ": ", l, '\n');
}
int main() {
read(T);
while (T--) solve();
#ifdef CLOCK
std::cerr << "\033[95m" << clock() * 1.0 / CLOCKS_PER_SEC << "s\n\033[90m";
#endif
return 0;
}
:::