bzoj1047[HAOI2007]理想的正方形 单调队列

题意:有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小。
早就看过这题了,解法其实很简单啦,但是我单调队列经常打错,然后没什么信心去搞这题= =最后想了想还是要锻炼一下自己就来刚了一波。
这应该是一个二维单调队列一样的东西,具体的话就是单调队列求出每一个点往右n个的最小最大值。
然后每一次对于一个n*n的矩形,枚举他的长宽起点(或者终点,都一样),再用两个单调队列求一列上的差值最小,具体就是让mx尽量小,mn尽量大。
感觉收获颇丰。。

#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e3+5;
const int M=1e6+5;
const int inf=1e9;
int mp[N][N],n,m,k,l,r,ans,mx[N][N],mn[N][N];
int L1[N],L2[N],R1[N],R2[N],d1[N][N],d2[N][N];
int l1,r1,l2,r2,q1[N],q2[N];
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    fo(i,1,n)
    fo(j,1,m)scanf("%d",&mp[i][j]),L1[i]=L2[i]=1;
    ans=inf;
    fo(i,1,n)
    {
        l1=l2=1,r1=r2=0;
        fd(j,m,1)
        {
            mx[i][j]=mn[i][j]=mp[i][j];
            while(l1<=r1&&q1[l1]-j>=k)l1++;
            while(l2<=r2&&q2[l2]-j>=k)l2++;

            while(r1>=l1&&mp[i][j]>mp[i][q1[r1]])r1--;
            q1[++r1]=j;
            if (l1<=r1)mx[i][j]=max(mx[i][j],mp[i][q1[l1]]);

            while(r2>=l2&&mp[i][j]
            q2[++r2]=j;
            if (l2<=r2)mn[i][j]=min(mn[i][j],mp[i][q2[l2]]);
        }
        fd(j,m-k+1,1)
        {
            while(L1[j]<=R1[j]&&i-d1[j][L1[j]]>=k)L1[j]++;
            while(L2[j]<=R2[j]&&i-d2[j][L2[j]]>=k)L2[j]++;
            while(L1[j]<=R1[j]&&mx[i][j]>mx[d1[j][R1[j]]][j])R1[j]--;
            while(L2[j]<=R2[j]&&mn[i][j]
            d1[j][++R1[j]]=i;
            d2[j][++R2[j]]=i;
            if (i>=k)
            {
                if (L1[j]<=R1[j]&&L2[j]<=R2[j])
                ans=min(ans,mx[d1[j][L1[j]]][j]-mn[d2[j][L2[j]]][j]);
            }
        }
    }

    printf("%d\n",ans);
}

你可能感兴趣的:(bzoj,单调队列,神奇脑洞题)