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;
}

 

posted @ 2017-10-23 19:32  或是七一  阅读(140)  评论(0编辑  收藏  举报