Codeforces 713D Animals and Puzzle(二维ST表+二分答案)

题目链接 Animals and Puzzle

题意  给出一个1e3 * 1e3的01矩阵,给出t个询问,每个询问形如x1,y1,x2,y2

你需要回答在以$(x1, y1)$为左上角,$(x1, y2)$为右下角的子矩阵中,最大的全1正方形的边长。

 

首先考虑DP预处理。

$f[i][j]$表示以$f[i][j]$为右下角的最大的全1正方形的边长。

则$f[i][j] = min(f[i - 1][j], f[i][j - 1], f[i - 1][j - 1]) + 1$

我们对$f[i][j]$构建一张二维ST表,使我们能在$O(1)$的时间复杂度内求出$f[i][j](x1 <= i <= x2, y1 <= j <= y2)$

但是这样直接查询$f[i][j]$的最大值$(x1 <= i <= x2, y1 <= j <= y2)$是不行的。

因为查询到的最大全1子正方形并不一定都在以$(x1, y1)$为左上角,$(x1, y2)$为右下角的子矩阵中。

所以我们需要另外想办法。

我们可以判断一个答案x是否存在,先求出合法的$f[i][j] >= x$的范围,

也就是说如果找到$f[i][j] >= x$了,这个找到的全1子正方形一定都在以$(x1, y1)$为左上角,$(x1, y2)$为右下角的子矩阵中。

显然x是单调的,所以我们可以二分答案。

时间复杂度$O(nmlognm + t)$

 

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)

const int N   = 1e3 + 3;
const int M   = 10;

int   f[N][N][M][M], lg[N];
int   n, m, x, q;

void ST(){
	rep(i, 2, 1e3 + 1) lg[i] = lg[i >> 1] + 1;
	rep(i, 1, n){
		for (int k = 1; (1 << k) <= m; ++k){
			rep(j, 1, m - (1 << k) + 1){
				f[i][j][0][k] = max(f[i][j][0][k - 1], f[i][j + (1 << (k - 1))][0][k - 1]);
			}
		}
	}

	for (int k1 = 1; (1 << k1) <= n; ++k1){
		rep(i, 1, n - (1 << k1) + 1){
			for (int k2 = 0; (1 << k2) <= m; ++k2){
				rep(j, 1, m - (1 << k2) + 1){
					f[i][j][k1][k2] = max(f[i][j][k1 - 1][k2], f[i + (1 << (k1 - 1))][j][k1 - 1][k2]);
				}
			}
		}
	}
}

int query(int x1, int y1, int x2, int y2){
	int k1 = lg[x2 - x1 + 1], k2 = lg[y2 - y1 + 1];
	x2 = x2 - (1 << k1) + 1;
	y2 = y2 - (1 << k2) + 1;
	return max(max(f[x1][y1][k1][k2], f[x1][y2][k1][k2]), max(f[x2][y1][k1][k2], f[x2][y2][k1][k2]));
}


int main(){

	freopen("1.txt", "r", stdin);
	freopen("2.txt", "w", stdout);
	
	scanf("%d%d", &n, &m);

	rep(i, 1, n){
		rep(j, 1, m){
			int x;
			scanf("%d", &x);
			if (x) f[i][j][0][0] = min(f[i - 1][j - 1][0][0],
						min(f[i - 1][j][0][0], f[i][j - 1][0][0])) + 1;
			else f[i][j][0][0] = 0;
		}
	}

	ST();
	scanf("%d", &q);
	while (q--){
		int x1, y1, x2, y2;
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		int l = 0, r = min(x2 - x1, y2 - y1) + 1, ans = 0;
		while (l <= r){
			int mid = (l + r) >> 1;
			if (query(x1 + mid - 1, y1 + mid - 1, x2, y2) >= mid) l = mid + 1, ans = mid;
			else r = mid - 1;
		}

		printf("%d\n", ans);
	}

	return 0;
}

 

posted @ 2017-09-22 19:19  cxhscst2  阅读(382)  评论(0编辑  收藏  举报