[BZOJ1047][HAOI2007]理想的正方形(单调队列)

211 篇文章 0 订阅
9 篇文章 0 订阅

题目描述

传送门

题解

由题目可知只有每一部分的最大值和最小值是有价值的。
单调队列只能处理一维的情况,二维的不好处理。那么我们可以考虑一维一维处理,比如说,先做列再做行。
可以将每一列每一个长度为n的区间用单调队列处理出来最大值和最小值,然后缩成一个点。
然后将每一行缩成的那些点每一个长度为n的区间再用单调队列处理出来最大值和最小值,这样就相当于求出了n*n的矩形的最大值和最小值统计答案即可。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 2005
#define inf 1000000001

int n,m,k,lmax,rmax,lmin,rmin,ans=inf;
int a[N][N];
struct hp{int Max,Min;}b[N][N];
struct hq{int id,val;}qmax[N],qmin[N];

void push(int i,int val)
{
    while (lmax<rmax&&qmax[rmax].val<val) --rmax;
    qmax[++rmax].id=i,qmax[rmax].val=val;
    while (lmin<rmin&&qmin[rmin].val>val) --rmin;
    qmin[++rmin].id=i,qmin[rmin].val=val;
}
hp pop(int i)
{
    while (lmax<rmax&&qmax[lmax+1].id<i) ++lmax;
    int Max=qmax[lmax+1].val;
    while (lmin<rmin&&qmin[lmin+1].id<i) ++lmin;
    int Min=qmin[lmin+1].val;
    return (hp){Max,Min};
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=n;++i)
        for (int j=1;j<=m;++j)
            scanf("%d",&a[i][j]);
    for (int i=1;i<=m;++i)
    {
        lmax=rmax=lmin=rmin=0;
        for (int j=1;j<=k;++j) push(j,a[j][i]);
        b[1][i]=pop(1);
        for (int j=k+1;j<=n;++j)
        {
            push(j,a[j][i]);
            b[j-k+1][i]=pop(j-k+1);
        }
    }
    for (int i=1;i<=n-k+1;++i)
    {
        lmax=rmax=lmin=rmin=0;
        for (int j=1;j<=k;++j) push(j,b[i][j].Max),push(j,b[i][j].Min);
        hp now=pop(1);ans=min(ans,now.Max-now.Min);
        for (int j=k+1;j<=m;++j)
        {
            push(j,b[i][j].Max);push(j,b[i][j].Min);
            now=pop(j-k+1);ans=min(ans,now.Max-now.Min);
        }
    }
    printf("%d\n",ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值