CF #574 (Div. 2) E. OpenStreetMap(单调队列)

该博客介绍了一种使用单调队列解决二维矩阵中特定大小子矩阵最小值之和的问题。首先,通过处理每一行降低问题的维度,然后对每列应用单调队列以获取最终答案。

题意:

根据公式得到一个n×m的矩阵,求矩阵中大小为a×b的所有子矩阵中最小值之和。
(1 ≤ n,m ≤ 3000, 1 ≤ a ≤ n, 1 ≤ b ≤ m)



分析:

用单调队列维护区间最值,但是单调队列是相对于一维的,这里是二维区域,那么就可以分开处理两个维度,先依次处理行,再处理列。

例如 n=4,m=4,a=2,b=2,矩阵为:
1 3 5 7
8 6 4 2
1 2 3 4
9 8 7 6

先对每行利用单调队列进行处理,将b=2降为b=1;

以第一行为例,对于1 3,最小为1,则(1, 1) = 1,对于3 5,最小为3,则(1, 2) = 3,以此类推。
那么矩阵就变为如下形式
1 3 5
6 4 2
1 2 3
8 7 6

然后再对每列利用单调队列处理,得到答案。



以下代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<queue>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=3e3+50;
void clr(deque<LL> &q)
{
    while(!q.empty())
        q.pop_front();
}
void ins(deque<LL> &q,LL v)
{
    while(!q.empty()&&q.back()>v)
        q.pop_back();
    q.push_back(v);
}
void del(deque<LL> &q,LL v)
{
    if(!q.empty()&&q.front()==v)
        q.pop_front();
}
int main()
{
    int n,m,a,b;
    LL pre,x,y,z;
    LL h[maxn][maxn];
    scanf("%d %d %d %d",&n,&m,&a,&b);
    scanf("%lld %lld %lld %lld",&pre,&x,&y,&z);
    //得到矩阵
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(i==1&&j==1)
                h[i][j]=pre;
            else
            {
                h[i][j]=(pre*x+y)%z;
                pre=h[i][j];
            }
        }
    }
    deque<LL> q;
    LL mi[maxn][maxn];
    //对行进行处理,得到矩阵mi
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=b;j++)
            ins(q,h[i][j]);
        mi[i][1]=q.front();
        for(int j=2;j<=m-b+1;j++)
        {
            del(q,h[i][j-1]);
            ins(q,h[i][j+b-1]);
            mi[i][j]=q.front();
        }
        clr(q);  //清空队列
    }
    LL ans=0;
    //再对列进行处理
    for(int j=1;j<=m-b+1;j++)
    {
        for(int i=1;i<=a;i++)
            ins(q,mi[i][j]);
        ans+=q.front();
        for(int i=2;i<=n-a+1;i++)
        {
            del(q,mi[i-1][j]);
            ins(q,mi[i+a-1][j]);
            ans+=q.front();
        }
        clr(q);    //清空队列
    }
    printf("%lld\n",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值