CF713D Animals and Puzzle 题解
有一个相当暴力的 $\mathcal{O}(nmt)$ 的做法是显然的,即对所有点求出以其为右下角的最大的全为 $1$ 的正方形的边长,记为 $f_{i,j}$。然后枚举询问的矩形中的点的 $f$ 的值,和边界判一下后的 $\max$ 就是答案了。
但是这样不好直接维护,由于矩形边界,并不能对 $f$ 取 $\max$。考虑转化为判定性问题,使用二分即可转化为求 $(x_1,y_1)$ 到 $(x_2-mid+1,y_2-mid+1)$ 的 $f$ 的极值是否大于等于 $mid$ 即可。由于查询已经带 $\log$ 了,使用 $\mathcal{O}(n\log^2n)-\mathcal{O}(1)$ 的二维 ST 表即可。
代码:
const int N = 1e3 + 10;
int n, m, Q, tot;
int a[N][N], log_2[N], f[11][11][N][N];
int qry(int xa, int ya, int xb, int yb) {
int k1 = log_2[xb - xa + 1], k2 = log_2[yb - ya + 1];
return max(f[k1][k2][xa][ya], max(max(f[k1][k2][xb - (1 << k1) + 1][ya], f[k1][k2][xa][yb - (1 << k2) + 1]), f[k1][k2][xb - (1 << k1) + 1][yb - (1 << k2) + 1]));
}
int main() {
ios
cin >> n >> m;
for (int i = 2; i <= max(n, m); i++) log_2[i] = log_2[i >> 1] + 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> a[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (a[i][j])
f[0][0][i][j] = min(min(f[0][0][i - 1][j], f[0][0][i][j - 1]), f[0][0][i - 1][j - 1]) + 1;
for (int k = 1; k <= 10; k++)
for (int i = 1; i <= n - (1 << k) + 1; i++)
for (int j = 1; j <= m; j++)
f[k][0][i][j] = max(f[k - 1][0][i][j], f[k - 1][0][i + (1 << (k - 1))][j]);
for (int k = 1; k <= 10; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m - (1 << k) + 1; j++)
f[0][k][i][j] = max(f[0][k - 1][i][j], f[0][k - 1][i][j + (1 << (k - 1))]);
for (int k1 = 1; k1 <= 10; k1++)
for (int k2 = 1; k2 <= 10; k2++)
for (int i = 1; i <= n - (1 << k1) + 1; i++)
for (int j = 1; j <= m - (1 << k2) + 1; j++)
f[k1][k2][i][j] = max(f[k1][k2 - 1][i][j], f[k1][k2 - 1][i][j + (1 << (k2 - 1))]);
cin >> Q;
while (Q--) {
int xa, ya, xb, yb;
cin >> xa >> ya >> xb >> yb;
int l = 1, r = min(xb - xa + 1, yb - ya + 1);
while (l <= r) {
int mid = (l + r) >> 1;
if (qry(xa + mid - 1, ya + mid - 1, xb, yb) >= mid) l = mid + 1;
else r = mid - 1;
}
cout << r << "\n";
}
return 0;
}