[ABC311G] One More Grid Task

[ABC311G] One More Grid Task

题目信息

题面翻译

给你一个 \(n\times m\) 的矩阵 \(a\),求:

\[\max_{1\leq l_1\leq r_1\leq n,1\leq l_2\leq r_2\leq m} (\sum_{l_1\leq i\leq r_1,l_2\leq j\leq r_2}a_{i,j} \times\min_{l_1\leq i\leq r_1,l_2\leq j\leq r_2}a_{i,j}) \]

题目描述

$ N\ \times\ M $ のグリッドがあり、上から $ i $ 行目、左から $ j $ 列目のマス $ (i,j) $ には非負整数 $ A_{i,j} $ が書かれています。
このグリッドのうち長方領域をひとつ選び、それを $ R $ とします。
厳密には、長方領域は以下の手順で選ばれます。

  • $ 1\ \le\ l_x\ \le\ r_x\ \le\ N,\ 1\ \le\ l_y\ \le\ r_y\ \le\ M $ なる整数 $ l_x,\ r_x,\ l_y,\ r_y $ を選ぶ。
  • このとき、整数 $ i,j $ が $ l_x\ \le\ i\ \le\ r_x $ かつ $ l_y\ \le\ j\ \le\ r_y $ を満たす、またその時に限って、マス $ (i,j) $ は $ R $ に含まれる。

適切に $ R $ を選ぶことによって、 $ f(R)\ = $ ( $ R $ 内のマスに書かれた整数の総和 ) $ \times $ ( $ R $ 内のマスに書かれた整数の最小値 ) として達成可能な最大値を求めてください。

输入格式

入力は以下の形式で標準入力から与えられる。

$ N $ $ M $ $ A_{1,1} $ $ A_{1,2} $ $ \dots $ $ A_{1,M} $ $ A_{2,1} $ $ A_{2,2} $ $ \dots $ $ A_{2,M} $ $ \vdots $ $ A_{N,1} $ $ A_{N,2} $ $ \dots $ $ A_{N,M} $

输出格式

答えを整数として出力せよ。

样例 #1

样例输入 #1

3 3
5 4 3
4 3 2
3 2 1

样例输出 #1

48

样例 #2

样例输入 #2

4 5
3 1 4 1 5
9 2 6 5 3
5 8 9 7 9
3 2 3 8 4

样例输出 #2

231

样例 #3

样例输入 #3

6 6
1 300 300 300 300 300
300 1 300 300 300 300
300 300 1 300 300 300
300 300 300 1 300 300
300 300 300 300 1 300
300 300 300 300 300 1

样例输出 #3

810000

提示

制約

  • 入力は全て整数
  • $ 1\ \le\ N,M\ \le\ 300 $
  • $ 1\ \le\ A_{i,j}\ \le\ 300 $

Sample Explanation 1

左上がマス $ (1,1) $ 、右下がマス $ (2,2) $ の長方領域を選ぶことで、 $ f(R)\ =\ (5+4+4+3)\ \times\ \min(5,4,4,3)\ =\ 48 $ が達成でき、これが達成可能な最大値です。

题目思路

算法一

我们可以考虑直接枚举左上角,枚举右下角,暴力求和以及暴力求最小值。

时间复杂度:\(O(n^3m^3)\).

算法二

算法一显然不行。我们可以提前预处理,用上数据结构(比如说树状数组、线段树树),直接求最小值和和。

时间复杂度:\(O(n^2m^2\log n\log m)\)

算法三

算法二显然也不行。考虑优化。

我们知道:任何矩阵的问题都可以压缩成一维问题。即枚举上界、枚举下界,求方案数。

预处理后,我们可以记录 \(left_i\)\(right_i\) 分别代表能延伸的最左边,能延伸的最右边。

直接使用单调栈维护即可。

时间复杂度:\(O(n^2m\log n)\)

代码

// LUOGU_RID: 161862093
#include<bits/stdc++.h>
#define int long long
#define left _zqh_
#define right flying_hq
using namespace std;
const int MAXN = 320;
const int LOGN = log2(MAXN)+5;
int n,m,matrix[MAXN][MAXN],ans;
void read(int &x){
	x = 0;int p = 1;char ch;
	do{
		ch = getchar();
		if(ch=='-') p = -1;
	}while(!isdigit(ch));
	while(isdigit(ch)){
		x*=10;
		x+=ch-'0';
		ch = getchar();
	}
	x*=p;
}
int sum[MAXN][MAXN],now[MAXN],left[MAXN],right[MAXN],q[MAXN],minn[MAXN];
class Segmenttree{
	private:
		struct segment{
			int l,r,min=0X3F3F3F3F;
		}tree[MAXN<<3];
		void pushup(int id){
			tree[id].min = min(tree[id*2].min,tree[id*2+1].min);
		}
	public:
		void build(int k,int l,int r){
			tree[k].l = l;
			tree[k].r = r;
			if(l==r) return;
			int mid = (l+r)>>1;
			build(k*2,l,mid);
			build(k*2+1,mid+1,r);
		}
		void change(int k,int p,int q){
			if(tree[k].l>p||tree[k].r<p) return;
			if(tree[k].l>=p&&tree[k].r<=p){
				tree[k].min = q;
				return; 
			}
			change(k*2,p,q);
			change(k*2+1,p,q);
			pushup(k);
		}
		int getmin(int k,int l,int r){
			if(tree[k].l>r||tree[k].r<l) return 0x3f3f3f3f;
			if(tree[k].l>=l&&tree[k].r<=r){
				return tree[k].min; 
			}
			return min(getmin(k*2,l,r),getmin(k*2+1,l,r));
		}
}tree[MAXN];
signed main(){
	read(n);read(m);
	for(int i = 1;i<=m;i++) tree[i].build(1,1,n);
	for(int i = 1;i<=n;i++){
		for(int j = 1;j<=m;j++){
			read(matrix[i][j]);
			sum[i][j] = sum[i-1][j]+matrix[i][j];
			tree[j].change(1,i,matrix[i][j]);
		}
	}
	for(int i = 1;i<=n;i++){
		for(int j = i;j<=n;j++){
			if(i == 1&&j== 2){
				int k = 0;
			}
			for(int k = 1;k<=m;k++){
				now[k] = sum[j][k]-sum[i-1][k];
				q[k] = q[k-1]+now[k];
				minn[k] = tree[k].getmin(1,i,j);
			}
			stack<pair<int,int>> st;
			for(int k = 1;k<=m;k++){
				while(!st.empty()&&st.top().second>=minn[k]) st.pop();
				if(st.empty()){
					left[k] = 1;
				}else{
					left[k] = st.top().first+1;
				}
				st.push({k,minn[k]});
			}
			while(!st.empty()){
				st.pop();
			}
			for(int k = m;k>=1;k--){
				while(!st.empty()&&st.top().second>=minn[k]) st.pop();
				if(st.empty()){
					right[k] = m;
				}else{
					right[k] = st.top().first-1;
				}
				st.push({k,minn[k]});
			}
			for(int k = 1;k<=m;k++){
				ans = max(ans,(q[right[k]]-q[left[k]-1])*minn[k]);
			}
		}
	}
	printf("%lld",ans);
	return 0;
}

tag

Atcoder题解
单调栈线段树树状数组数据结构

posted @ 2024-06-11 11:42  GuTongXing  阅读(28)  评论(0)    收藏  举报