暑假牛客多校赛第三场F.Planting Trees(单调队列)

F.Planting Trees

传送门

The semester is finally over and the summer holiday is coming. However, as part of your university’s graduation requirement, you have to take part in some social service during the holiday. Eventually, you decided to join a volunteer group which will plant trees in a mountain.

To simplify the problem, let’s represent the mountain where trees are to be planted with an N×N grid. Let’s number the rows 1 to N from top to bottom, and number the columns 1 to N from left to right. The elevation of the cell in the i t h i_th ith row and j t h j_th jth column is denoted by a i , j a_{i,j} ai,j. Your leader decides that trees should be planted in a rectangular area within the mountain and that the maximum difference in elevation among the cells in that rectangle should not exceed M. In other words, if the coordinates of the top-left and the bottom-right corners of the rectangle are ( x 1 , y 1 ) (x_1,y_1) (x1,y1)and ( x 2 , y 2 ) (x_2,y_2) (x2,y2), then the condition ∣ a i , j − a k , l ∣ |a_{i,j} - a_{k,l}| ai,jak,l ≤ \le M must hold for x 1 ≤ i , k ≤ x 2 ,   y 1 ≤ j , l ≤ y 2 x_1 \le i,k \le x_2, \ y_1 \le j,l \le y_2 x1i,kx2, y1j,ly2 . Please help your leader calculate the maximum possible number of cells in such a rectangle so that he’ll know how many trees will be planted.

输入描述:

The input contains multiple cases. The first line of the input contains a single integer T   ( 1 ≤ T ≤ 1000 ) T \ (1 \le T \le 1000) T (1T1000), the number of cases.
For each case, the first line of the input contains two integers N   ( 1 ≤ N ≤ 500 ) a n d M   ( 0 ≤ M ≤ 1 0 5 ) N\ (1 \le N \le 500) and M\ (0 \le M \le 10^5) N (1N500)andM (0M105). The following N lines each contain N integers, where thej-th integer in thei-th line denotes a i , j   ( 1 ≤ a i , j ≤ 1 0 5 ) a_{i,j} \ (1 \le a_{i,j} \le 10^5) ai,j (1ai,j105).It is guaranteed that the sum of N 3 N^3 N3 over all cases does not exceed 25 ⋅ 1 0 7 25 \cdot 10^7 25107.

输出描述:

For each case, print a single integer, the maximum number of cells in a valid rectangle.
示例1

输入

2
2 0
1 2
2 1
3 1
1 3 2
2 3 1
3 2 1

输出

1
4

题目大意:

给出一个N*M的矩阵,矩阵的值是海拔的高度,问你满足海拔高度差不小于k能组成的最大矩阵是多少。

题目思路:

这道题要用到单调队列
已经熟练掌握的大佬点击这里→捷径

让我们先了解一下单调队列(一般用来求区间窗口问题)
单调队列有单调递增和单调递减两种,一般来讲,队列的队首是整个队列的最大值或最小值
单调队列的实现:(双端队列,一段入,两端输出)
具体步骤:

  1. 若队列为空,将A[i]从队尾入队
  2. 若队列不为空,将比A[i]大的元素都从队尾弹出,然后把A[i]入队
  3. 若队列不为空且A[i]大于队尾,则直接从队尾把A[i]入队

实现一般采用双端队列;(两端l,r)

//维护区间长度为s的最小值单调队列
	int l=1,r=0; //左端点和右端点
    for(int i=1;i<=n;i++){
        if(i-que[l]+1>a&&l<=r)l++; //若区间太大了则左端点右移
        while(mp[i][x]<mp[que[r]][x]&&l<=r) r--; //若存入的数小于第一个数,队列中最右边的数,则弹出
        que[++r]=i; //将新的端点存入队列中。  
        dp[i][x]=mp[que[l]][x]; //记录此时队列的首个元素(一定是队列中最小元素)
    }

大家可以先做一下这道cf类似题(mizp大佬提供的题目,大家快膜拜%%%)
cf:E. OpenStreetMap
给你一个n * m的矩形,问你它所有大小为a*b的子矩形的最小值之和是多少
mizp大佬的题解

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int n,m,a,b;
ll go,x,y,z;
int mp[3005][3005],que[3005],dp[3005][3005];

void row(int x){
    int l=1,r=0;
    for(int i=1;i<=n;i++){
        if(i-que[l]+1>a&&l<=r)l++;
        while(mp[i][x]<mp[que[r]][x]&&l<=r) r--;
        que[++r]=i;
        dp[i][x]=mp[que[l]][x];
    }
}

void col(int x){
    int l=1,r=0;
    for(int i=1;i<=m;i++){
        if(i-que[l]+1>b&&l<=r) l++;
        while(dp[x][i]<dp[x][que[r]]&&l<=r) r--;
        que[++r]=i;
        mp[x][i]=dp[x][que[l]];
    }
}

int main(){

    scanf("%d %d %d %d",&n,&m,&a,&b);
    scanf("%lld %lld %lld %lld",&go,&x,&y,&z);

    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            mp[i][j]=go;
            go=(go*x+y)%z;
        }

    for(int i=1;i<=m;i++) row(i);
    for(int i=1;i<=n;i++) col(i);

    ll ans=0;
    for(int i=a;i<=n;i++)
        for(int j=b;j<=m;j++)
            ans+=mp[i][j];

    printf("%lld\n",ans);
    return 0;
}
对于这道题,很明显要维护上下左右边界的最大值和最小值。 所以我们可以枚举上边界,然后下边界向下遍历,同时用单调队列维护左边界和右边的最大和最小值使之满足条件。即可求出最大面积。

在这里插入图片描述
我们左右边界的维护只需要维护上边界到下边界的最大值和最小值。
代码复杂度上边界O(n)*下边界O(n)*单调队列O(n)=O( n 2 n^2 n2)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;

int T,N,M,a[505][505];
int minq[505],maxq[505];//单调队列维护最大值和最小值
int mina[505],maxa[505]; //维护上下边界的最大值和最小值

void init(){
    for(int k=1;k<=N;k++){
        mina[k]=INF;
        maxa[k]=-INF;
    }
}

int main(){
    scanf("%d",&T);
    while(T--){
        int 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++){  //上边界
            init();  //初始化maxa和mina数组
            for(int j=i;j<=N;j++){   //下边界
                for(int k=1;k<=N;k++){    //维护上边界到下边界的最大值和最小值
                    maxa[k]=max(maxa[k],a[j][k]);
                    mina[k]=min(mina[k],a[j][k]);
                }
                int l=1,l1=1,r1=0,l2=1,r2=0; //左右边界及单调队列的左右指针
                for(int r=1;r<=N;r++){   //对每一行单调队列维护最大值和最小值
                    while(l1<=r1&&maxa[r]>=maxa[maxq[r1]]) r1--; //维护最大值
                    while(l2<=r2&&mina[r]<=mina[minq[r2]]) r2--; //维护最小值
                    maxq[++r1]=r; //将新值加入单调队列中
                    minq[++r2]=r; //将新值加入单调队列中
                    while(l<=r&&maxa[maxq[l1]]-mina[minq[l2]]>M){  //如果最大值-最小值大于M,不满足条件,则删到满足为止
                        l++;
                        if(maxq[l1]<l) l1++;
                        if(minq[l2]<l) l2++;
                    }
                ans=max(ans,(r-l+1)*(j-i+1));//上边界乘下边界
                }
            }
         }

        cout<<ans<<endl;
    }
}

遇到区间最值问题,可以用单调队列来维护一定长度的区间最值。将复杂度降到O(n)二维只需枚举一个边界,再进行单调队列即可.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值