zoukankan      html  css  js  c++  java
  • [BZOJ 1047] [HAOI2007] 理想的正方形 【单调队列】

    题目链接:BZOJ - 1047

    题目分析

    使用单调队列在 O(n^2) 的时间内求出每个 n * n 正方形的最大值,最小值。然后就可以直接统计答案了。

    横向有 a 个单调队列(代码中是 Q[1] 到 Q[a] ),维护每行当前枚举区间的单调队列。

    纵向一个单调队列(代码中是 Q[0] ),求出当前枚举区间的每行的单调队列后,就得到了每行的这个区间的最小值(最大值),就相当于一个长度为行数的数组,然后纵向做单调队列,求出的就是正方形的最值了。

    代码

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    const int MaxN = 1000 + 5, INF = 999999999;
    
    int a, b, n, Ans;
    int Map[MaxN][MaxN], Q[MaxN][MaxN], F[MaxN], Head[MaxN], Tail[MaxN], Min[MaxN][MaxN], Max[MaxN][MaxN];
    //Q[0]是纵向的单调队列 
    
    inline int gmin(int a, int b) {return a < b ? a : b;}
    inline int gmax(int a, int b) {return a > b ? a : b;}
    
    void Get_Min() 
    {
    	for (int i = 1; i <= a; ++i) 
    	{
    		Head[i] = 1;
    		Tail[i] = 0;
    	}
    	for (int i = 1; i <= b; ++i) 
    	{
    		for (int j = 1; j <= a; ++j) 
    		{
    			if (i > n && Head[j] <= Tail[j] && Q[j][Head[j]] == i - n) ++Head[j];
    			while (Head[j] <= Tail[j] && Map[j][i] < Map[j][Q[j][Tail[j]]]) --Tail[j];
    			Q[j][++Tail[j]] = i; 
    		}	
    		if (i >= n) 
    		{
    			Head[0] = 1; Tail[0] = 0;
    			for (int j = 1; j <= a; ++j)
    			{
    				F[j] = Map[j][Q[j][Head[j]]];
    				if (j > n && Head[0] <= Tail[0] && Q[0][Head[0]] == j - n) ++Head[0];
    				while (Head[0] <= Tail[0] && F[j] < F[Q[0][Tail[0]]]) --Tail[0];
    				Q[0][++Tail[0]] = j;
    				if (j >= n) Min[j][i] = F[Q[0][Head[0]]]; 
    			}
    		}
    	}
    }
    
    void Get_Max() 
    {
    	for (int i = 1; i <= a; ++i) 
    	{
    		Head[i] = 1;
    		Tail[i] = 0;
    	}
    	for (int i = 1; i <= b; ++i) 
    	{
    		for (int j = 1; j <= a; ++j) 
    		{
    			if (i > n && Head[j] <= Tail[j] && Q[j][Head[j]] == i - n) ++Head[j];
    			while (Head[j] <= Tail[j] && Map[j][i] > Map[j][Q[j][Tail[j]]]) --Tail[j];
    			Q[j][++Tail[j]] = i; 
    		}
    		if (i >= n) 
    		{
    			Head[0] = 1; Tail[0] = 0;
    			for (int j = 1; j <= a; ++j)
    			{
    				F[j] = Map[j][Q[j][Head[j]]];
    				if (j > n && Head[0] <= Tail[0] && Q[0][Head[0]] == j - n) ++Head[0];
    				while (Head[0] <= Tail[0] && F[j] > F[Q[0][Tail[0]]]) --Tail[0];
    				Q[0][++Tail[0]] = j;
    				if (j >= n) Max[j][i] = F[Q[0][Head[0]]]; 
    			}
    		}
    	}
    }
    
    int main()
    {
    	scanf("%d%d%d", &a, &b, &n);
    	for (int i = 1; i <= a; ++i) 
    		for (int j = 1; j <= b; ++j) 
    			scanf("%d", &Map[i][j]);
    	Get_Min();
    	Get_Max();
    	Ans = INF;
    	for (int i = n; i <= a; ++i) 
    		for (int j = n; j <= b; ++j) 
    			Ans = gmin(Ans, Max[i][j] - Min[i][j]);
    	printf("%d
    ", Ans);
    	return 0;
    }
    

      

  • 相关阅读:
    luogu P1979 华容道
    bzoj1096: [ZJOI2007]仓库建设
    bzoj3437: 小P的牧场
    bzoj1597: [Usaco2008 Mar]土地购买
    bzoj3156: 防御准备
    Miller-Rabin与Pollard-Rho备忘
    [PKUSC2018]星际穿越(倍增)
    [PKUSC2018]神仙的游戏(FFT)
    [PKUSC2018]最大前缀和(DP)
    [BZOJ5465][APIO2018]选圆圈(KD-Tree)
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4319732.html
Copyright © 2011-2022 走看看