[二分][二维前缀和]Valiant‘s New Map Codeforces1731D

Game studio "DbZ Games" wants to introduce another map in their popular game "Valiant". This time, the map named "Panvel" will be based on the city of Mumbai.

Mumbai can be represented as n×mn×m cellular grid. Each cell (i,j)(i,j) (1≤i≤n1≤i≤n; 1≤j≤m1≤j≤m) of the grid is occupied by a cuboid building of height ai,jai,j.

This time, DbZ Games want to make a map that has perfect vertical gameplay. That's why they want to choose an l×ll×l square inside Mumbai, such that each building inside the square has a height of at least ll.

Can you help DbZ Games find such a square of the maximum possible size ll?

Input

Each test contains multiple test cases. The first line contains the number of test cases tt (1≤t≤10001≤t≤1000). Description of the test cases follows.

The first line of each test case contains two positive integers nn and mm (1≤n≤m1≤n≤m; 1≤n⋅m≤1061≤n⋅m≤106).

The ii-th of next nn lines contains mm integers ai,1,ai,2,…,ai,mai,1,ai,2,…,ai,m (1≤ai,j≤1061≤ai,j≤106) — heights of buildings on the ii-th row.

It's guaranteed that the sum of n⋅mn⋅m over all test cases doesn't exceed 106106.

Output

For each test case, print the maximum side length ll of the square DbZ Games can choose.

Example

input

4

2 2

2 3

4 5

1 3

1 2 3

2 3

4 4 3

2 1 4

5 6

1 9 4 6 5 8

10 9 5 8 11 6

24 42 32 8 11 1

23 1 9 69 13 3

13 22 60 12 14 17

output

2

1

1

3

题意: 给出一个n*m的矩阵,找出其中最大的一个k*k的子矩阵,满足其中所有的数组都大于等于k。

分析: 很显然可以二分,关键在于判断是否存在满足题意的边长为k的子矩阵。判断的时候可以先扫一遍矩阵,将其中大于k的元素修改为k,然后求二位前缀和,扫描所有边长为k的子矩阵,若其二维区间和大小为k^3,那么就说明存在满足题意的子矩阵。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#define int long long
using namespace std;

vector<vector<int>> a;
vector<vector<int>> cpy;
vector<vector<int>> s;
int n, m;

bool check(int x){
	a = cpy;
	s.clear();
	for(int i = 0; i <= n; i++){
		vector<int> b;
		for(int j = 0; j <= m; j++){
			b.push_back(0);
			if(a[i][j] > x) a[i][j] = x;
		}
		s.push_back(b);
	}
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			s[i][j] = a[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1];
	//枚举左上角位置 
	for(int i = 1; i+x-1 <= n; i++)
		for(int j = 1; j+x-1 <= m; j++){
			if(s[i+x-1][j+x-1]-s[i-1][j+x-1]-s[i+x-1][j-1]+s[i-1][j-1] == x*x*x)
				return true;
		}
	return false;
}

signed main(){
	int T;
	cin >> T;
	while(T--){
		a.clear();
		scanf("%lld%lld", &n, &m);
		for(int i = 0; i <= n; i++){
			vector<int> b;
			b.push_back(0);
			if(i == 0)
				for(int j = 1; j <= m; j++)
					b.push_back(0);
			else{
				for(int j = 1; j <= m; j++){
					int t;
					scanf("%d", &t);
					b.push_back(t);
				}
			}
			a.push_back(b);
		}
		cpy = a;
		int l = 1, r = min(n, m), ans = -1;
		while(l <= r){
			int mid = l+r>>1;
			if(check(mid)){
				ans = mid;
				l = mid+1;
			}
			else r = mid-1;
		}
		printf("%lld\n", ans);
	}


    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值