题目描述
AP
神牛准备给自己盖一座很华丽的宫殿。于是,他看中了一块
N∗M
的矩形空地。空地中每个格子都有自己的海拔高度。
AP
想让他的宫殿的平均海拔在海平面之上(假设海平面的高度是
0
,平均数都会算吧?)。而且,
输入
第一行为
N
和
输出
输出一行,表示宫殿最大面积。
样例输入
3
4
−10
8
样例输出
4
提示
对于
对于
100%
的数据,
N,M≤200
解法
(首先,宫殿也是矩形= =)
这题 n 6 和 n 4 暴力很显然
在
n 4
的思路上进行优化。保留枚举子矩阵的左右端的列,在行的枚举上做优化。对于一个确定的
L、R
,可以用行前缀和
O(1)
知道第
i
行在这个区间内的值
我们要找一个平均值大于
0
的区间,就是需要找到一段和大于
对
s[i][R]−s[i][L−1]
做前缀和,即
S[k]=∑ k i=1 (s[i][R]−s[i][L−1])
问题现在就转化成为找一个 i∈[0,k) 使得 S[k]−S[i]>0 且i尽量小。
因为我们现在求一个区间的值可以用
S
数组相减得到结果,所以注意到一个性质,如果对于一个数
所有比
于是乎……选择性地保留一些
(这是一个单调栈)
然后更新答案时二分查找单调栈中第一个可行元素。
时间代价
n 3 log(n)
(题外话一:我还写了个
n 3 log(lim)
的线段树硬上失败……)
(题外话二:数据有点水,暴力优化一下就跑过了……)
代码
#include<stdio.h>
#define ll long long
#define N 250
#define lim 1000000000000
int n,m,ans,l,r,mid,q[N];
long long s[N][N],S[N];
inline int max(const int &a,const int &b){return (a<b)?b:a;}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1,a;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&a),s[i][j]=s[i][j-1]+a;
for (int i=1;i<=m;i++) for (int j=i,t=0;j<=m;j++,t=0) for (int k=1;k<=n;k++)
{
if ((S[k]=S[k-1]+s[k][j]-s[k][i-1])<S[q[t]]) q[++t]=k;
for (l=0,r=t,mid=(l+r)>>1;l<r;mid=(l+r)>>1) if (S[q[mid]]<S[k]) r=mid;
else l=mid+1;
ans=max(ans,(k-q[l])*(j-i+1));
}
printf("%d\n",ans);
}