单调队列
bzoj1047 理想的正方形
题目大意:求a*b的矩阵中一个n*n的子矩阵,使得子矩阵的最大值和最小值的差最小。
思路:一开始认为能用二维线段树a掉,但lcomyn大神写了一下,结果T了,于是就寻找新的写法。借鉴了斜率优化的思路,发现单调队列可以优越的做到O(ab)的求出整个矩阵中每个点左面延伸n位的最值。我们用行上的单调队列维护出这个之后,再从列上单调队列一下,就能求出一个n*n子矩阵的最值了,然后比较一下,输出答案。注意这里单调队列中的是下标(这样写可能会有一些好处),所以写的时候要对应到数组中。
#include<iostream> #include<cstdio> #include<cstring> #define maxnode 1010 using namespace std; int q1[maxnode]={0},q2[maxnode]={0},map[maxnode][maxnode]={0},h1,h2,t1,t2,n, maxn[maxnode][maxnode]={0},minn[maxnode][maxnode]={0}; void qmin(int i,int j) { while(h1<=t1&&q1[h1]<=j-n) ++h1; while(h1<=t1&&map[i][q1[t1]]>=map[i][j]) --t1; q1[++t1]=j; } void qmax(int i,int j) { while(h2<=t2&&q2[h2]<=j-n) ++h2; while(h2<=t2&&map[i][q2[t2]]<=map[i][j]) --t2; q2[++t2]=j; } void wmin(int i,int j) { while(h1<=t1&&q1[h1]<=i-n) ++h1; while(h1<=t1&&minn[q1[t1]][j]>=minn[i][j]) --t1; q1[++t1]=i; } void wmax(int i,int j) { while(h2<=t2&&q2[h2]<=i-n) ++h2; while(h2<=t2&&maxn[q2[t2]][j]<=maxn[i][j]) --t2; q2[++t2]=i; } int main() { int i,j,a,b,ans; scanf("%d%d%d",&a,&b,&n); for (i=1;i<=a;++i) for (j=1;j<=b;++j) scanf("%d",&map[i][j]); /*求点(i,j)左面n内的最大最小值*/ for (i=1;i<=a;++i) { h1=h2=1;t1=t2=0; for (j=1;j<=b;++j) { qmin(i,j);qmax(i,j); if (j>=n) { minn[i][j]=map[i][q1[h1]];maxn[i][j]=map[i][q2[h2]]; } } } /*求列j为右边界的矩形的ans*/ ans=0x7fffffff; for (j=1;j<=b;++j) { h1=h2=1;t1=t2=0; for (i=1;i<=a;++i) { wmin(i,j);wmax(i,j); if (j>=n&&i>=n&&maxn[q2[h2]][j]-minn[q1[h1]][j]<ans) ans=maxn[q2[h2]][j]-minn[q1[h1]][j]; } } printf("%d\n",ans); }
xjoi T2
题目大意:给定一个nm的矩阵,求最大最小值差不超过k的子矩阵的个数。
思路:穷举i~j行,然后扫出每一列的最大最小值,用单调队列维护这些最大最小值,插入的时候就是从队尾比较着插入,但是查询的时候还要注意k的限制,所以先把队首那些不满足的弹出(但是要注意:如果这次没有弹出队首,符合的区间可能是上次更新时的位置,而不是0),然后累加答案。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxm 405 #define LL long long using namespace std; int ai[maxm][maxm],q1[maxm],q2[maxm],maxn[maxm],minn[maxm]; int main(){ int n,m,k,i,j,a,b,h1,h2,t1,t2;LL ans=0; scanf("%d%d%d",&n,&m,&k); for (i=1;i<=n;++i) for (j=1;j<=m;++j) scanf("%d",&ai[i][j]); for (i=1;i<=n;++i){ memset(maxn,0,sizeof(maxn)); memset(minn,127,sizeof(minn)); for (j=i;j<=n;++j){ h1=h2=1;t1=t2=0; for (b=a=1;a<=m;++a){ maxn[a]=max(maxn[a],ai[j][a]); while(h1<=t1&&maxn[a]>=maxn[q1[t1]]) --t1; minn[a]=min(minn[a],ai[j][a]); while(h2<=t2&&minn[a]<=minn[q2[t2]]) --t2; q1[++t1]=q2[++t2]=a; while(h1<=t1&&maxn[q1[h1]]-k>minn[a]){b=max(b,q1[h1]+1);++h1;} while(h2<=t2&&minn[q2[h2]]+k<maxn[a]){b=max(b,q2[h2]+1);++h2;} ans+=(LL)(a-b+1); } } }cout<<ans<<endl; }