【NOIP2016模拟赛No.1】 牛宫

题目描述

AP  神牛准备给自己盖一座很华丽的宫殿。于是,他看中了一块 NM  的矩形空地。空地中每个格子都有自己的海拔高度。 AP  想让他的宫殿的平均海拔在海平面之上(假设海平面的高度是 0  ,平均数都会算吧?)。而且,AP 希望他的宫殿尽量大,能够容纳更多的人来膜拜他。请问 AP  的宫殿最后会有多大?

输入

第一行为 N  M 。之后 N  行,每行M 个数,描述的空地的海拔。

输出

输出一行,表示宫殿最大面积。

样例输入

3  2 
4  0 
10  8 
2  2 

样例输出

4 

提示

对于30% 的数据, N,M50  ;
对于 100%  的数据, N,M200 

解法
(首先,宫殿也是矩形= =)

这题 n 6   n 4   暴力很显然

n 4   的思路上进行优化。保留枚举子矩阵的左右端的列,在行的枚举上做优化。对于一个确定的 LR  ,可以用行前缀和 O(1)  知道第 i  行在这个区间内的值s[i][R]s[i][L1] 
我们要找一个平均值大于 0  的区间,就是需要找到一段和大于0 的行。
s[i][R]s[i][L1]  做前缀和,即 S[k]= k i=1 (s[i][R]s[i][L1]) 

问题现在就转化成为找一个 i[0,k)  使得 S[k]S[i]>0  且i尽量小。

因为我们现在求一个区间的值可以用 S  数组相减得到结果,所以注意到一个性质,如果对于一个数i[0,k) ,还存在 j[0,i)  S[j]<=S[i]  ,那么还要 i  干什么……
所有比S[i] 大的,都比 S[j]  大,与 j  得到的答案也都更优。
于是乎……选择性地保留一些S 来更新答案,在 k  从上往下扫时,如果S[k] 比最后一个记录的 S  还小,就把S[k] 也记录上
(这是一个单调栈)
然后更新答案时二分查找单调栈中第一个可行元素。
时间代价 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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值