2019牛客暑期多校训练营(第三场)F Planting Trees (单调队列)

https://ac.nowcoder.com/acm/contest/883/F
题意:给定一个 n × n n×n n×n的矩阵,求面积最大的子矩阵,使得该子矩阵中任意两点 ( i , j ) , ( k , l ) (i,j),(k,l) (i,j),(k,l)的权值 ∣ a i , j − a k , l ∣ ≤ m |a_{i,j}-a_{k,l}|≤m ai,jak,lm 1 ≤ n ≤ 500 , 0 ≤ m ≤ 1 0 5 , 1 ≤ a i , j ≤ 1 0 5 1≤n≤500,0≤m≤10^5,1≤a_{i,j}≤10^5 1n500,0m105,1ai,j105

首先枚举上下边界,并维护每一列的最大值和最小值。
然后枚举右边界,同时维护最大值和最小值两个单调队列,最大值的单调队列是递减的,最小值的单调队列是递增的,这样我们就可以利用这两个单调队列得到符合条件的最小的左边界,从而在 O ( n 3 ) O(n^3) O(n3)时间复杂度内实现。
要注意的是STL的deque很慢,要手写单调队列,不然会T。

#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
#include <list>
using namespace std;

struct node{
    int id,v;
    node(int _id=0,int _v=0)
    {
        id=_id;v=_v;
    }
};

int n,m,T,ans,a[550][550],mi[550],ma[550];
node miq[550],maq[550];

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&a[i][j]);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                mi[j]=a[i][j];
                ma[j]=a[i][j];
            }
            for(int j=i;j<=n;j++)
            {
                for(int k=1;k<=n;k++)
                {
                    mi[k]=min(a[j][k],mi[k]);
                    ma[k]=max(a[j][k],ma[k]);
                }
                int mif=1,mib=0,maf=1,mab=0;
                int l=1;
                for(int k=1;k<=n;k++)
                {
                    while(mif<=mib&&miq[mib].v>mi[k])
                        mib--;
                    miq[++mib]=node(k,mi[k]);
                    while(maf<=mab&&maq[mab].v<ma[k])
                        mab--;
                    maq[++mab]=node(k,ma[k]);
                    while(mif<=mib&&maf<=mab&&maq[maf].v-miq[mif].v>m)
                    {
                        if(maq[maf].id<miq[mif].id)
                        {
                            l=maq[maf].id+1;
                            maf++;
                        }
                        else if(maq[maf].id>miq[mif].id)
                        {
                            l=miq[mif].id+1;
                            mif++;
                        }
                        else if(maq[maf].id==miq[mif].id)
                        {
                            l=maq[maf].id+1;
                            maf++;mif++;
                        }
                    }
                    ans=max(ans,(k-l+1)*(j-i+1));
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值