题解 P2219 【[HAOI2007]修筑绿化带】
说看代码理解的你们够了
相信泥萌都是从试炼场刷过来的吧?所以这道题泥萌一定做过了。回顾一下怎么做的呢?二维的单调队列做法:先就某一维上对原始数据进行处理,在对另一维上对前一维的数据进行处理,就得到了所需要的东西。
回到这题,也是同样的做法,下边是我解题时的思路:
-
预处理出以A[i,j]、B[i,j]分别为右下角的花坛(C*D)、大矩形(A*B)的肥沃度和
-
按行,利用单调队列求出P[i,j]=以A[i, j-B+1+D→j-1]中的最小值,即以(i, j-B+1+D→j-1)为右下角的花坛肥沃度的最小值。
-
按列,利用单调队列求出Q[i,j]=以P[i-A+1+C→i-1, j]中的最小值,即以(i-A+1+C→i-1, j-B+1+D→j-1) 为右下角的花坛肥沃度的最小值,显然,这些花坛包含在了以(i,j)为右下角的大矩形中;
-
显然B[i,j]-Q[i,j]即为以(i,j)为右下角的绿化带的最大肥沃值。
时间复杂度 O(nm)。代码实现中n、m与题意是反的。欢迎来hack
#include <bits/stdc++.h>
using namespace std;
const int N=1005;
int n,m,A,B,C,D,hd,tl,q[N];
int s[N][N],a[N][N],b[N][N],P[N][N],Q[N][N];
int main() {
scanf("%d%d %d%d%d%d",&n,&m,&A,&B,&C,&D);
for(int i=1; i<=n; ++i) {
for(int j=1; j<=m; ++j) {
scanf("%d",&s[i][j]);
s[i][j]=s[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1];
}
}
for(int i=C+1; i<n; ++i) {
for(int j=D+1; j<m; ++j) {
a[i][j]=s[i][j]-s[i-C][j]-s[i][j-D]+s[i-C][j-D];
}
}
for(int i=A; i<=n; ++i) {
for(int j=B; j<=m; ++j) {
b[i][j]=s[i][j]-s[i-A][j]-s[i][j-B]+s[i-A][j-B];
}
}
for(int i=C+1; i<n; ++i) {
hd=1, tl=0;
for(int j=D+1; j<m; ++j) {
while(hd<=tl && q[hd]<j-B+2+D) hd++; //*
while(hd<=tl && a[i][q[tl]]>=a[i][j]) tl--;
q[++tl]=j;
if(j>=B-1) P[i][j+1]=a[i][q[hd]];
}
}
for(int j=B; j<=m; ++j) {
hd=1, tl=0;
for(int i=C+1; i<n; ++i) {
while(hd<=tl && q[hd]<i-A+2+C) hd++;
while(hd<=tl && P[q[tl]][j]>=P[i][j]) tl--;
q[++tl]=i;
if(i>=A-1) Q[i+1][j]=P[q[hd]][j];
}
}
int ans=0;
for(int i=A; i<=n; ++i) {
for(int j=B; j<=m; ++j) {
ans=max(ans,b[i][j]-Q[i][j]);
}
}
printf("%d\n",ans);
return 0;
}
* 原来的区间[j-B+1+D, j-1]是对于j来说的;但此处我们在j的位置时更新j+1,所以范围调整为[j-B+2+D, j]。下同。