[P2219][HAOI2007]修筑绿化带

Link:

P2219 传送门

Solution:

先对每个点算出以$(i,j)$为矩形右下角时的权值和

问题就转化为对于以$(i,j)$为右下角的$a*b$的矩形中的最小值

这时模型和 [BZOJ 1047]理想的正方形 就基本相同了,做两次单调队列就好了

Tip:

1、$c*d$处于$a*b$边界的情况不能选,注意边界判断

2、算矩阵和时是用$pre[i-a][j-b]$而不是$pre[i-a-1][j-b-1]$……

Code:

#include <bits/stdc++.h>

using namespace std;
const int MAXN=1005;
int n,m,a,b,c,d,x,l,r,q[MAXN],res=0;
int pre[MAXN][MAXN],mx[MAXN][MAXN],mn[MAXN][MAXN],ori[MAXN][MAXN];

int get1(int i,int j,int x)
{return pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+x;}
int get2(int i,int j,int l,int r)
{return pre[i][j]+pre[i-l][j-r]-pre[i][j-r]-pre[i-l][j];}

int main()
{
    scanf("%d%d%d%d%d%d",&n,&m,&a,&b,&c,&d);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&x),pre[i][j]=get1(i,j,x);
    memset(mn,0x3f,sizeof(mn));memset(ori,0x3f,sizeof(ori));
    for(int i=a;i<=n;i++)
        for(int j=b;j<=m;j++)
            mx[i][j]=get2(i,j,a,b);
    for(int i=c;i<=n;i++)
        for(int j=d;j<=m;j++)
            ori[i][j]=min(ori[i][j],get2(i,j,c,d));
    
    for(int i=c;i<=n;i++)
    {
        l=1;r=0;
        for(int j=d;j<=m;j++)
        {
            while(l<=r&&j-q[l]>b-d-2) l++;//注意去掉边界情况 
            while(l<=r&&ori[i][j]<ori[i][q[r]]) r--;
            q[++r]=j;mn[i][j]=min(mn[i][j],ori[i][q[l]]);
        }
    }
    
    for(int j=d;j<=m;j++)
    {
        l=1;r=0;
        for(int i=c;i<=n;i++)
        {
            while(l<=r&&i-q[l]>a-c-2) l++;
            while(l<=r&&mn[i][j]<mn[q[r]][j]) r--;
            q[++r]=i;
            if(i+1>=a&&j+1>=b&&i+1<=n&&j+1<=m)
                res=max(res,mx[i+1][j+1]-mn[q[l]][j]);
        }
    }
    
    printf("%d",res);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/newera/p/9343945.html