子矩阵 蓝桥杯 单调队列 二维滑动窗口

//本题考查单调队列的应用,核心是维护每一行/列的个滑动窗口的最值 
//对于每一行(列),使用单调队列预处理出每个长为b(a)的滑动窗口的最值
//再枚举原矩阵每一列,处理出每个子矩阵的最值即可
//注:单调队列是保证队内元素递增/减有序的队列,在入队时弹出破坏顺序的元素 
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N=1e3+10;
const int mod=998244353;

deque<int>dq;//双端队列实现单调队列 
int matrix[N][N];//存储原始矩阵 
int n,m,a,b;
int MAX[N][N],MIN[N][N];//MAX[i][j]存储第i行在[j-(b-1),j]范围内的最大值,MIN同理 

void get_max(int A[],int B[],int len,int k)
//对于长度为len的数组A,求长度为k的各滑动窗口的最大值,结果存入数组B 
{
    dq.clear();//清空双端队列 
    for(int i=1;i<=len;i++)//遍历全部元素 
    {
        while(!dq.empty()&&A[i]>=A[dq.back()])//队列非空,当前元素大于队尾 
        {
            dq.pop_back();//队尾元素不断出队直至当前队尾更大 
        }    
        dq.push_back(i);//当前元素下标入队 
        if(dq.front()<i-k+1)dq.pop_front();//队首存储的下标移出滑动窗口左端,调整队首 
        B[i]=A[dq.front()];//保存当前滑动窗口的最大值 
    } 
    //例如对于A[10] = 4 1 9 8 2 7 6 10 5 3,设k=5, 则B[10] = 4 4 9 9 9 9 9 10 10 10
    //从B[5]开始每个B[i]都表示区间[i-(k-1),i]内的最大值 
}

void get_min(int A[],int B[],int len,int k)//同上,略 
{
    dq.clear();
    for(int i=1;i<=len;i++)
    {
        while(!dq.empty()&&A[i]<=A[dq.back()])dq.pop_back();        
        dq.push_back(i);
        if(dq.front()<i-k+1)dq.pop_front();
        B[i]=A[dq.front()];
    }    
}

int main()
{
    ll sum=0;
    scanf("%d%d%d%d",&n,&m,&a,&b);
    for(int i=1;i<=n;i++)//输入原始矩阵 
    {
        for(int j=1;j<=m;j++)scanf("%d",&matrix[i][j]);    
    }    
    
    //对原矩阵的每一行,长为m,滑动窗口长为b,求各滑动窗口的最大/小值,存入MAX/MIN矩阵的第i行 
    for(int i=1;i<=n;i++)get_max(matrix[i],MAX[i],m,b);
    for(int i=1;i<=n;i++)get_min(matrix[i],MIN[i],m,b);
    
    int temp[N],matrix_max[N],matrix_min[N];
    for(int i=b;i<=m;i++)//从第b列开始枚举每一列i 
    {
        //对于原矩阵的每一行j,取其区间[i-(b-1),i]内的最大值,存入temp 
        for(int j=1;j<=n;j++)temp[j]=MAX[j][i];
        //对这n个最大值,滑动窗口长为a,求各滑动窗口的最大值,即各子矩阵的最大值 
        get_max(temp,matrix_max,n,a); 
        for(int j=1;j<=n;j++)temp[j]=MIN[j][i];//同上,略 
        get_min(temp,matrix_min,n,a);
        //经过以上步骤,在j>=a时,matrix_max[j]存储[j-(a-1),j]区间内最大值,matrix_min同理
         
        for(int j=a;j<=n;j++)//累加即可 
        {
            sum=(sum+(ll)(matrix_max[j]*matrix_min[j]%mod))%mod;
        }
    }
    printf("%lld\n",sum);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值