码迷,mamicode.com
首页 > 其他好文 > 详细

[HAOI2007] 理想的正方形

时间:2018-04-11 15:38:41      阅读:182      评论:0      收藏:0      [点我收藏+]

标签:简单   需要   std   两种   haoi2007   math   break   cst   入队   

  这道题用到单调队列以及降维处理。

  之前我并没有学过单调队列,第一眼看到直接想法是二维线段树,但是我并不会写二维线段树,而且时间复杂度也很玄乎。

  于是补了一波单调队列。

  简单说一点。好久了我好像快忘了。

  单调队列的作用是求一个滑动区间的最值。

  比如对于一个长度为1000的数列,求以每个数开始长度为7的区间的最小值。就好像这个区间在均匀滑动。

  单调队列的思想是没用的直接丢掉。

  假设我要求区间长度为 m 的区间最小值,现在当前区间有 L 个数已经在队列 Q 中且满足单调递增

  现在考虑要新加入的这个元素 x 。

  有两种情况。

  1:x > Q [ L ] 。直接将 x 入队。

  2:x <= Q [ L ] 。这时候因为我们要求的是最小值且区间在滑动,而元素 Q [ L ] 先于 x 入队,将来也一定先于 x 出队 。也就是说元素 Q [ L ] 在队列中的时候,元素 x 一定在队列中,且因为 x <= Q [ L ],Q [ L ] 对区间最值已经不会有贡献了,多以我们将 Q [ L ] 出队,且对于队列中的所有比 x 大或等的元素都出队,一样的道理。最后将 x 入队。

  每次输出队首元素即可。

  对于求区间最大值我们保证队列元素递减

  注意区间在滑动,所以我们要记录队列中的元素在原数列中的位置,如果已经不在区间范围内就将其出队。区间每次滑动一,每次最多因为这种情况出队一个元素,且该元素一定为队首元素。

  数列中每个数最多入队1次,出队1次。

  时间复杂度O ( n ) 。

  有了单调队列,来解决这道问题。

  1:我们对每行求出长度为 n 的滑动区间最值记录在 maxx[i][j] ,minx[i][j] 中。

  此时 maxx[i][j] 存储的是第 i 行区间 [ j , j + n - 1 ] 的最大值,minx[i][j]为最小值。

  2:我们对每一列 ( 对 minx[][] 和 maxx[][] ) 求出长度为 n 的滑动区间最值。

  在求的时候直接比较 max - min 的最大值并记录,最后输出就可以了。

  这就是降维思想,第二次跑单调队列时出来的最值已经是 n * n 区间的最值了。

  我表达能力不好。

  单调队列在写的时候细节是需要注意的。

 

// q.c

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int M=1000+10;
int a,b,n,w[M][M],wMin[M][M],wMax[M][M],h1,t1,h2,t2,qMin[M],qMax[M],pos1[M],pos2[M],ans=2*(int)1e9+7;
void reset() {
	h1=h2=1; t1=t2=0;
	memset(qMin,0,sizeof(qMin));
	memset(qMax,0,sizeof(qMax));
	memset(pos1,0,sizeof(pos1));
	memset(pos2,0,sizeof(pos2));
}
void insert1(int x,int y,int z,int r) {
	while(t1>=h1&&qMin[t1]>=r) t1--;
	qMin[++t1]=r,pos1[t1]=z;
}
void insert2(int x,int y,int z,int r) {
	while(t2>=h2&&qMax[t2]<=r) t2--;
	qMax[++t2]=r,pos2[t2]=z;
}
int main() {
	freopen("square.in","r",stdin);
	freopen("square.out","w",stdout);
	scanf("%d%d%d",&a,&b,&n);
	for(int i=1;i<=a;i++)
		for(int j=1;j<=b;j++)
			scanf("%d",&w[i][j]);
	for(int i=1;i<=a;i++) {
		reset();
		for(int j=1;j<=n;j++) {
			insert1(i,j,j,w[i][j]);
			insert2(i,j,j,w[i][j]);
		}
		for(int j=n+1;j<=b+1;j++) {
			wMin[i][j-n]=qMin[h1];
			wMax[i][j-n]=qMax[h2];
			if(j==b+1) break;
			insert1(i,j,j,w[i][j]);
			insert2(i,j,j,w[i][j]);
			if(pos1[h1]<=j-n) h1++;
			if(pos2[h2]<=j-n) h2++;
		}
	}
	for(int j=1;j<=b-n+1;j++) {
		reset();
		for(int i=1;i<=n;i++) {
			insert1(i,j,i,wMin[i][j]);
			insert2(i,j,i,wMax[i][j]);
		}
		for(int i=n+1;i<=a+1;i++) {
			ans=min(ans,qMax[h2]-qMin[h1]);
			if(i==a+1) break;
			insert1(i,j,i,wMin[i][j]);
			insert2(i,j,i,wMax[i][j]);
			if(pos1[h1]<=i-n) h1++;
			if(pos2[h2]<=i-n) h2++;
		}
	}
	printf("%d\n",ans);
	return 0;
}

 

[HAOI2007] 理想的正方形

标签:简单   需要   std   两种   haoi2007   math   break   cst   入队   

原文地址:https://www.cnblogs.com/qjs12/p/8794766.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!