歌名 - 歌手
0:00

    【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);
    }
    
    posted @ 2018-05-21 12:09  无尽的蓝黄  阅读(140)  评论(0编辑  收藏  举报