P11503 [NordicOI 2018] Nordic Camping
二分 + DS 妙题
思路
首先我们可以发现。若有一个点 \((x, y)\),则我们可以通过二分求出以 \((x, y)\) 为左上角的最大空正方形的边长(记为 \(b[i][j]\)),check 就是判断以 \((x, y)\) 为左上角的边长为 \(mid\) 的正方形是否为空,其单调性在于我们的条件使得边长更大的正方形完全包括边长更小的正方形,若边长更小的正方形不为空则边长更大的正方形一定不为空。并且,某个作为答案的正方形一定是以某个点为左上角的最大空正方形。
然后,一种方案是。我们分析以 \((x, y)\) 为左上角的最大空正方形对答案的贡献,发现应该把整个正方形中的所有点的答案都和当前正方形的面积取 \(\max\),于是树套树即可。
第二种方案是,我们考虑对于每一个询问去求解答案。此时我们又可以二分答案。Check 判断以 \((x, y)\) 为右下角的边长为 \(mid\) 正方形中的 \(\max b[i][j]\) 是否大于等于 \(mid\),起单调性与上类似,同样是考虑问题之间是包含的。 二维 ST 表维护即可。
Code
#include<bits/stdc++.h>using namespace std;const int N = 2e3 + 5, L = 12;int n, m, q;
int a[N][N], sum[N][N], f[N << 1][N << 1][L], lg[N]; inline int S(int x1, int y1, int x2, int y2) {return sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];
}inline int Query(int x1, int y1, int x2, int y2) {int k = lg[x2 - x1 + 1], a = 1 << k;return max({f[x1][y1][k], f[x2 - a + 1][y1][k], f[x1][y2 - a + 1][k], f[x2 - a + 1][y2 - a + 1][k]});
}int main() {ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n >> m;for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {char ch;cin >> ch;a[i][j] = ch == '#';sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j]; }}for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {int l = 0, r = min(n - i + 1, m - j + 1);while (l < r) {int mid = l + r + 1 >> 1;S(i, j, i + mid - 1, j + mid - 1) ? r = mid - 1 : l = mid;}f[i + N][j + N][0] = (!a[i][j]) * l;}}for (int i = 2; i <= max(n, m); i++) {lg[i] = lg[i >> 1] + 1;}for (int i = 1; i < L; i++) {int a = 1 << i, b = 1 << i - 1;for (int j = 1; j + a - 1 <= n + N; j++) {for (int k = 1; k + a - 1 <= m + N; k++) {f[j][k][i] = max({f[j][k][i - 1], f[j + b][k][i - 1], f[j][k + b][i - 1], f[j + b][k + b][i - 1]}); }}}cin >> q;for (int x, y; q--; ) {cin >> x >> y;x += N, y += N;int l = 0, r = max(n, m);while (l < r) {int mid = l + r + 1 >> 1;Query(max(1, x - mid + 1), max(1, y - mid + 1), x, y) >= mid ? l = mid : r = mid - 1; }cout << l * l << '\n';}return 0;
}