题意:
根据公式得到一个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;
}

该博客介绍了一种使用单调队列解决二维矩阵中特定大小子矩阵最小值之和的问题。首先,通过处理每一行降低问题的维度,然后对每列应用单调队列以获取最终答案。
&spm=1001.2101.3001.5002&articleId=96480040&d=1&t=3&u=76c8e5da8b5948409bd9e2480f48fbcb)
818

被折叠的 条评论
为什么被折叠?



