题目链接
思路:要想知道ab这个子矩阵最小的元素,我们可以维护两个单调队列,首先我们维护n行的单调队列q,每行单调队列存的就是每b个区间的最小值,当遍历到b列以后,我们再维护一个单调队列p,它维护的是这n行的q队列的队头元素每a个区间的最小值,如此ab子矩阵的最小就得出来了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e3+10;
int q[maxn][maxn],n,m,a,b;
pair<int,int>p[maxn];
ll g[maxn*maxn],x,y,z,s[maxn],e[maxn],h[maxn][maxn],ans;
int main()
{
scanf("%d%d%d%d",&n,&m,&a,&b);
scanf("%lld%lld%lld%lld",&g[0],&x,&y,&z);
for(int i=1;i<=n*m;++i)
g[i]=(g[i-1]*x+y)%z;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
h[i][j]=g[(i-1)*m+j-1];
for(int i=1;i<=n;++i) s[i]=1,e[i]=0;
for(int j=1;j<=m;++j)
{
for(int i=1;i<=n;++i)
{
while(s[i]<=e[i]&&h[i][q[i][e[i]]]>=h[i][j]) e[i]--;
q[i][++e[i]]=j;
while(j-q[i][s[i]]>=b) s[i]++;
}
if(j>=b)
{
int head=1,tail=0;
for(int i=1;i<=n;++i)
{
while(head<=tail&&h[p[tail].first][p[tail].second]>=h[i][q[i][s[i]]]) tail--;
p[++tail]={i,q[i][s[i]]};
while(i-p[head].first>=a) head++;
if(i>=a) ans+=h[p[head].first][p[head].second];
}
}
}
printf("%lld\n",ans);
}
这里再贴一个大佬的单调队列模板,很好用。。。
#include<bits/stdc++.h>//单调队列又称为滑动窗口
using namespace std;
#define ls rt<<1
#define rs (rt<<1)+1
#define ll long long
#define fuck(x) cout<<#x<<" "<<x<<endl;
const int maxn=1e6+10;
int d[4][2]={1,0,-1,0,0,1,0,-1};
int a[maxn],q[maxn],ans1[maxn],ans2[maxn];
int main()
{
int n,m,h,t;
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%d",&(a[i]));
h=1,t=0;
for(int i=1;i<=n;i++)//求m区间内最小值 以i为末尾的m区间 维护增队列
{
while(h<=t&&a[q[t]]>=a[i]) t--;//删去队尾的无用元素
q[++t]=i;
while(i-q[h]+1>=m+1) h++;//队头删去在所需区间外的元素
if(i>=m)
ans1[i-m+1]=a[q[h]];
}
h=1,t=0;
for(int i=1;i<=n;i++)//求m区间内最大值 以i为末尾的m区间
{
while(h<=t&&a[q[t]]<=a[i]) t--;//删去队尾的无用元素
q[++t]=i;
while(i-q[h]+1>=m+1) h++;//队头删去在所需区间外的元素
if(i>=m)
ans2[i-m+1]=a[q[h]];
}
for(int i=1;i+m-1<=n;i++) printf("%d ",ans1[i]);
cout<<endl;
for(int i=1;i+m-1<=n;i++) printf("%d ",ans2[i]);
cout<<endl;
return 0;
}