P2216理想的正方形题解
又是一道题解满了的题
按照题意,我们需要维护一个 \(n\times n\) 大小的矩阵内的最大最小值。
显然一个二维的东西不好直接维护。
我们可以先维护行的最值,然后再用单调队列维护连续\(n\)行的最值。(如果暂时不理解这句话请先看下面的操作。)
设 \(mx[i][j],mn[i][j]\) 分别表示 \(a[i][j]\) ~ \(a[i][j+n-1]\) 中的最大/最小值,用单调队列维护,一次维护一行,共维护 \(n\) 次,复杂度\(O(n^2)\)。
然后按列维护,维护 \(mx[i][j]\) ~ \(mx[i+n-1][j]\) 的最大值以及 \(mn[i][j]\) ~ \(mn[i+n-1][j]\) 的最小值,作差取 \(max\)。这样就可以维护 \(n\times n\)的矩阵了。
\(Code\)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<deque>
#define pa pair<int,int>
#define rg register
#define ls (k<<1)
#define rs (k<<1|1)
using namespace std;
typedef long long ll;
const double eps=1e-13;
inline ll read(){
char ch=getchar();
ll x=0;bool f=0;
while(ch<'0'||ch>'9')
{
if(ch=='-') f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return f?-x:x;
}
int n,a,b,jz[1009][1009],mx[1009][1009],ans=2147483647,mn[1009][1009];
deque<int> q[3];//q[1]:最大值,q[2]:最小值
void push(int opt,int ps,int i){
if(opt==1){
while(!q[1].empty()&&jz[i][q[1].back()]<jz[i][ps]) q[1].pop_back();
q[1].push_back(ps);
}
if(opt==2){
while(!q[2].empty()&&jz[i][q[2].back()]>jz[i][ps]) q[2].pop_back();
q[2].push_back(ps);
}
if(opt==3){
while(!q[1].empty()&&mx[q[1].back()][i]<mx[ps][i]) q[1].pop_back();
q[1].push_back(ps);
}
if(opt==4){
while(!q[2].empty()&&mn[q[2].back()][i]>mn[ps][i]) q[2].pop_back();
q[2].push_back(ps);
}
}
int main()
{
a=read();b=read();n=read();
for(int i=1;i<=a;i++) for(int j=1;j<=b;j++) jz[i][j]=read();
for(int i=1;i<=a;i++){//按行维护
while(!q[1].empty()) q[1].pop_front();//把上一行的清空
while(!q[2].empty()) q[2].pop_front();
for(int j=1;j<n;j++) push(1,j,i),push(2,j,i);
for(int j=n;j<=b;j++){
while(!q[1].empty()&&q[1].front()<j-n+1) q[1].pop_front();
while(!q[2].empty()&&q[2].front()<j-n+1) q[2].pop_front();
push(1,j,i);push(2,j,i);
mx[i][j-n+1]=jz[i][q[1].front()];
mn[i][j-n+1]=jz[i][q[2].front()];
}
}
for(int i=1;i<=b-n+1;i++){//因为只有[1,b-n+1]之间的列有mx,mn值,所以只循环到b-n+1
while(!q[1].empty()) q[1].pop_front();//清空上一列
while(!q[2].empty()) q[2].pop_front();
for(int j=1;j<n;j++) push(3,j,i),push(4,j,i);
for(int j=n;j<=a;j++){
while(!q[1].empty()&&q[1].front()<j-n+1) q[1].pop_front();
while(!q[2].empty()&&q[2].front()<j-n+1) q[2].pop_front();
push(3,j,i);push(4,j,i);
ans=min(ans,mx[q[1].front()][i]-mn[q[2].front()][i]);
}
}
printf("%d\n",ans);
}