【bzoj1047】[HA蛤OI2007]理想的正方形【单调队列】
题目传送门
xfz学长给我们讲了单调栈和单调队列,这方面内容以前写过PJT4的跳房子等等。
事实上单调栈和单调队列挺好理解的,单调栈是栈内元素单调的栈,单调队列是队内元素单调的队列(废话)。
先写一道非常裸的模板题。这道题以最大值为例,先一次单调队列求出每一行每个位置向右连续n个数的最大值,再一次单调队列把之前求出的一维最大值用同样的方法和并为二维最大值。单调队列实现方法就是先把左端点不在当前考虑区间内的丢掉,再比较当前位置与队尾的大小,若大于等于则把队尾丢掉,把当前位置的元素放进去。求出最大值和最小值最后统计即可。细节详见代码。(其实没啥细节,我这样的菜鸡也能1A)
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1005;
int n,m,x,head,tail,ans=1e9,q[N],a[N][N],f[N][N],maxn[N][N],minn[N][N];
int main(){
scanf("%d%d%d",&n,&m,&x);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
}
}
for(int i=1;i<=n;i++){
head=1,tail=0;
for(int j=1;j<=x;j++){
while(head<=tail&&a[i][j]>=a[i][q[tail]]){
tail--;
}
q[++tail]=j;
}
f[i][1]=a[i][q[head]];
for(int j=x+1;j<=m;j++){
while(head<=tail&&q[head]<j-x+1){
head++;
}
while(head<=tail&&a[i][j]>=a[i][q[tail]]){
tail--;
}
q[++tail]=j;
f[i][j-x+1]=a[i][q[head]];
}
}
for(int i=1;i<=m-x+1;i++){
head=1,tail=0;
for(int j=1;j<=x;j++){
while(head<=tail&&f[j][i]>=f[q[tail]][i]){
tail--;
}
q[++tail]=j;
}
maxn[1][i]=f[q[head]][i];
for(int j=x+1;j<=n;j++){
while(head<=tail&&q[head]<j-x+1){
head++;
}
while(head<=tail&&f[j][i]>=f[q[tail]][i]){
tail--;
}
q[++tail]=j;
maxn[j-x+1][i]=f[q[head]][i];
}
}
for(int i=1;i<=n;i++){
head=1,tail=0;
for(int j=1;j<=x;j++){
while(head<=tail&&a[i][j]<=a[i][q[tail]]){
tail--;
}
q[++tail]=j;
}
f[i][1]=a[i][q[head]];
for(int j=x+1;j<=m;j++){
while(head<=tail&&q[head]<j-x+1){
head++;
}
while(head<=tail&&a[i][j]<=a[i][q[tail]]){
tail--;
}
q[++tail]=j;
f[i][j-x+1]=a[i][q[head]];
}
}
for(int i=1;i<=m-x+1;i++){
head=1,tail=0;
for(int j=1;j<=x;j++){
while(head<=tail&&f[j][i]<=f[q[tail]][i]){
tail--;
}
q[++tail]=j;
}
minn[1][i]=f[q[head]][i];
for(int j=x+1;j<=n;j++){
while(head<=tail&&q[head]<j-x+1){
head++;
}
while(head<=tail&&f[j][i]<=f[q[tail]][i]){
tail--;
}
q[++tail]=j;
minn[j-x+1][i]=f[q[head]][i];
}
}
for(int i=1;i<=n-x+1;i++){
for(int j=1;j<=m-x+1;j++){
ans=min(ans,maxn[i][j]-minn[i][j]);
}
}
printf("%d\n",ans);
return 0;
}