Atcoder ABC311G One More Grid Task

发布时间 2023-07-24 07:59:20作者: lhzawa

可以想到枚举最小值同时算出包含其的最大矩阵和。
考虑枚举行的上下界,再枚举最小值然后求出最大的列的范围,因为 \(a_{i, j}\ge 1\) 列的范围越广矩阵和也越大。

考虑如何算出列的范围,令第 \(i\) 列在选中的行的范围内的最小值为 \(mn_i\),则对于 \(mn_i\) 的列
的范围 \([l, r]\) 需满足里面所有的数都不能小于 \(mn_i\),即对于 \(l\le j\le r\) 的任意一个 \(j\) 都有 \(mn_j\ge mn_i\)
用单调栈维护一下即可,对于数的和做一个前缀和即可。

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

// lhzawa(https://www.cnblogs.com/lhzawa/)
// Problem: G - One More Grid Task
// Contest: AtCoder - Toyota Programming Contest 2023#4(AtCoder Beginner Contest 311)
// URL: https://atcoder.jp/contests/abc311/tasks/abc311_g
// Memory Limit: 1024 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
const int N = 3e2 + 10;
int a[N][N];
int w[N], h[N], _h[N];
int stk[N], top;
int lw[N];
int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			scanf("%d", &a[i][j]);
		}
	}
	long long ans = 0;
	for (int l = 1; l <= n; l++) {
		for (int i = 1; i <= m; i++) {
			w[i] = 0x3f3f3f3f, h[i] = 0;
		}
		for (int r = l; r <= n; r++) {
			for (int i = 1; i <= m; i++) {
				w[i] = min(w[i], a[r][i]), _h[i] = _h[i - 1] + a[r][i], h[i] += _h[i];
			}
			stk[1] = top = lw[1] = 1;
			for (int i = 2; i <= m; i++) {
				lw[i] = i;
				for (; top && w[i] <= w[stk[top]]; ans = max(ans, 1ll * w[stk[top]] * (h[i - 1] - h[lw[stk[top]] - 1])), lw[i] = lw[stk[top]], top--);
				stk[++top] = i;
			}
			for (; top; ans = max(ans, 1ll * w[stk[top]] * (h[m] - h[lw[stk[top]] - 1])), top--);
		}
	}
	printf("%lld\n", ans);
	return 0;
}