【NOIP2016提高A组模拟10.15】最大化

题目

这里写图片描述

分析

枚举两个纵坐标i、j,接着表示枚举区域的上下边界,
设对于每个横坐标区域的前缀和和为(s_l),枚举k,
显然当(s_k>s_l)时,以(i,k)为左上角,(j,k)为右下角的矩阵一定合法。
这里写图片描述
k从小到大,维护一个单调队列,
显然当(l1<l2)
如果(s_{l1}<s_{l2}),l2一定对答案没有贡献,就不将其加入单调队列。
对于一个k,在单调队列中二分,枚举出一个最小的位置,并且(s_k>s_l)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
const int N=305;
using namespace std;
long long sum[N][N],num[N];
int d[N],tot,n,m;
int ans;
inline int read(long long &n)
{
	char ch=' ';
	int q=0,w=1;
	for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
	if(ch=='-') w=-1,ch=getchar();
	for(;ch>='0' && ch<='9';ch=getchar()) q=q*10+ch-48;n=q*w;
	return n;
}
inline long long sum1(int x,int y,int x1,int y1)
{
	return sum[x1][y1]-sum[x-1][y1]-sum[x1][y-1]+sum[x-1][y-1];
}
int main()
{
	scanf("%d%d",&n,&m);
	int i,j,k;
	long long p;
	for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)
		{
			read(sum[i][j]);
			sum[i][j]=sum[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
		}
	ans=0;
	int l=1,r=tot,mid;
	for(i=1;i<=n;i++)
		for(j=n;j>=i && (j-i+1)*m>ans;j--)
		{
			int e=j-i+1;
			tot=1;
			for(k=1;k<=m;k++)
			{
				p=sum1(i,1,j,k);
				if((j-i+1)*k>ans) 
				{
					l=1;
					r=tot;
					while(l<r)
					{
						mid=(l+r)/2;
						if(num[mid]<p)
							r=mid;
						else l=mid+1;
					}
					if(num[l]<p) ans=max(ans,e*(k-d[l]));
				}
				if(p<num[tot]) 
				{
					d[++tot]=k;
					num[tot]=p;
				}
			}
		}
	printf("%d",ans);
}
原文地址:https://www.cnblogs.com/chen1352/p/9066564.html