1. 程式人生 > >【NOIP2016提高A組模擬10.15】最大化

【NOIP2016提高A組模擬10.15】最大化

pan include scanf 沒有 algorithm %d 矩陣 枚舉 tdi

題目

技術分享圖片

分析

枚舉兩個縱坐標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);
}

【NOIP2016提高A組模擬10.15】最大化