思路:对于某一行来说,我们只需要维护[1,b],[2,b+1],[3,b+2]...[m-b+1,m]的最小值,然后再对列进行维护即可,最后的矩阵的和就是答案。如何维护[1,b],[2,b+1],[3,b+2]...[m-b+1,m]的最小值呢?我一开始的思路是把区间的数字放到multiset中,然后set.begin()存的就是最小值,每次区间向右移,则增加新元素,删掉之前区间最左边的元素,但是这样做的复杂度是n*m*log,于是TLE。于是换了单调栈。对于某一行,我们维护一个长度最大为b的单调递增栈,每次遇到一个元素则把这个元素放入栈中,同时,栈中比此元素大的数字则会被pop出丢弃掉,最后答案就是栈底元素的值。处理完行之后,再处理每一列即可。这时,c[i][j]的值是以(i,j)为右下角,长宽各为a和b的矩形的最小值。
#include<iostream>
#include<cstdio>
#include<set>
using namespace std;
const int MAXN=3002;
int a[MAXN][MAXN];
int b[MAXN][MAXN];
int c[MAXN][MAXN];
int q[MAXN];
int n,m;
int aa,bb;
typedef long long ll;
ll g,x,y,z;
int main(){
scanf("%d%d%d%d",&n,&m,&aa,&bb);
scanf("%lld%lld%lld%lld",&g,&x,&y,&z);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
a[i][j]=g;
g=(g*x+y)%z;
}
}
for(int i=1;i<=n;i++){
int l=1,r=0;
for(int j=1;j<=m;j++){
if(l<=r&&j-q[l]+1>bb){
l++;
}
while(l<=r&&a[i][j]<=a[i][q[r]])r--;
q[++r]=j;
b[i][j]=a[i][q[l]];
}
}
for(int j=1;j<=m;j++){
int l=1,r=0;
for(int i=1;i<=n;i++){
if(l<=r&&i-q[l]+1>aa){
l++;
}
while(l<=r&&b[i][j]<=b[q[r]][j])r--;
q[++r]=i;
c[i][j]=b[q[l]][j];
}
}
ll ans=0;
for(int i=aa;i<=n;i++){
for(int j=bb;j<=m;j++){
ans+=c[i][j];
}
}
printf("%lld\n",ans);
return 0;
}