bzoj 1047
枚举右下角,问题转化成求以(x,y)为右下角的n*n正方形中的极值。
我们可以先把这个n*n正方形拆成n行n列。
用单调队列可以很快求出每一个位置往左n个的极值。
然后存储下来再用单调队列再求一次,合起来就是一个n*n正方形。
感觉很难说清楚,可以配合代码理解。
#include<cstdio> #include<cctype> #include<algorithm> using namespace std; int read(){ char c; while(!isdigit(c=getchar())); int x=c-'0'; while(isdigit(c=getchar())) x=x*10+c-'0'; return x; } int m[1001][1001],_1[1001][1001],_2[1001][1001],q1[1001],q2[1001]; int main(){ int a=read(),b=read(),n=read(),ans=2e9; for(int i=1;i<=a;i+=1) for(int j=1;j<=b;j+=1) m[i][j]=read(); for(int i=1;i<=a;i+=1){ int h1=0,t1=0,h2=0,t2=0; for(int j=1;j<=b;j+=1){ while(h1<t1 && q1[h1]<=j-n) h1++; while(h2<t2 && q2[h2]<=j-n) h2++; while(h1<t1 && m[i][j]<=m[i][q1[t1-1]]) t1--; q1[t1++]=j; while(h2<t2 && m[i][j]>=m[i][q2[t2-1]]) t2--; q2[t2++]=j; _1[i][j]=m[i][q1[h1]]; _2[i][j]=m[i][q2[h2]]; } } for(int j=n;j<=b;j+=1){ int h1=0,t1=0,h2=0,t2=0; for(int i=1;i<=a;i+=1){ while(h1<t1 && q1[h1]<=i-n) h1++; while(h2<t2 && q2[h2]<=i-n) h2++; while(h1<t1 && _1[i][j]<=_1[q1[t1-1]][j]) t1--; q1[t1++]=i; while(h2<t2 && _2[i][j]>=_2[q2[t2-1]][j]) t2--; q2[t2++]=i; if(i>=n) ans=min(_2[q2[h2]][j]-_1[q1[h1]][j],ans); } } printf("%d",ans); return 0; }