**E. OpenStreetMap
theme:给定n*m的矩阵,求所有大小为r*c的子矩阵的最小值之和。1≤n,m≤3000, 1≤a≤n, 1≤b≤m
solution:借助双端队列求出每个子矩阵的最小值。
对于一维数组求长度为x的子串的最小值,deque存的是下标,每次向右移动则将deque中比左边界小的出队,由于是求最小值,所以从后遍历deque,把队列中值>=当前值的pop_back()出,这样队列中就存着唯一的元素即为在该区间内且值最小。
对于二维数组,可以先一行一行得求,第一遍得到的minn[i][j]表示第i行元素中,以j为最后一个位置的长度为c的最小值。再类似地一列一列求,这时不再是与原数组比较,而是与minn数组比较,求出到第j列时,第i行即以上共r行中最小的minn[i][j]即为该区域的最小值。
#include<bits/stdc++.h>
using namespace std;
#define far(i,t,n) for(int i=t;i<n;++i)
#define endl "\n"
#define spa " "
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
ll g[9000010];
ll minn[3005][3005];
ll a[3005][3005];
deque<int>dq;
void getG(int n,ll x,ll y,ll z)
{
for(int i=1;i<=n;++i)
g[i]=(g[i-1]*x%z+y)%z;
}
int main()
{
int n,m,r,c;
cin>>n>>m>>r>>c;
ll x,y,z;
cin>>g[0]>>x>>y>>z;
getG(n*m,x,y,z);
int cnt=0;
far(i,1,n+1)
far(j,1,m+1)
a[i][j]=g[cnt++];
far(i,1,n+1)
{
while(!dq.empty())dq.pop_back();
far(j,1,m+1)
{
while(!dq.empty()&&j-dq.front()>=c)dq.pop_front();
while(!dq.empty()&&a[i][dq.back()]>=a[i][j])dq.pop_back();
dq.push_back(j);
if(j>=c)
minn[i][j]=a[i][dq.front()];//算出每行长度为b的子串的最小值
}
}
ll ans=0;
far(j,1,m+1)
{
while(!dq.empty())dq.pop_back();
far(i,1,n+1)
{
while(!dq.empty()&&i-dq.front()>=r)dq.pop_front();
while(!dq.empty()&&minn[dq.back()][j]>=minn[i][j])dq.pop_back();
dq.push_back(i);
if(i>=r)
ans+=minn[dq.front()][j];
}
}
cout<<ans<<endl;
}
/*
5
9 3 5 7 3
5 8 1 4 5
*/