• [HAOI2007]理想的正方形


    Description

    BZOJ1047

    Solution

    首先易得暴力DP:(f[i][j][k])表示以(i,j)为左上角的边长为k的正方形的最大值(或最小值),可得(f[i][j][k] = min{a[i][j], f[i+1][j][k-1], f[i][j+1][k-1], f[i+1][j+1][k-1]}),复杂度高达(O(abn)),数量级约为(10^9),无法通过数据。

    考虑优化,由于是最值问题,即RMQ问题,由此可以想到ST表,复杂度(O(ablog n)),基本能卡过时限,但是空间限制比较悬,还需要改进(其实用滚动数组就能优化到能过)。

    继续优化,考虑区间最值问题的另一种解决办法:单调队列,首先将行分别维护单调队列,求出第i行中区间([j,j+n-1])中的最值,记为(m[i][j]),然后对m中的每一列维护单调队列,求出第j列中区间([i,i+n-1])的最值,记为(f[i][j]),则(f[i][j])为以(i,j)为左上角的边长为k的正方形的最值,直接统计答案即可,时空复杂度均为(O(ab)),已经足够优秀。

    Code

    #include <cstdio>
    #include <algorithm>
    
    const int N = 1010;
    
    int a[N][N], mxa[N][N], mna[N][N], mxb[N][N], mnb[N][N];
    int mx[N], mxhd, mxtl, mn[N], mnhd, mntl, mxp[N], mnp[N];
    int n, m, k;
    
    int main() {
    	scanf("%d%d%d", &n, &m, &k);
    	for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) scanf("%d", &a[i][j]);
    	for (int i = 1; i <= n; ++i) {
    		mxhd = mxtl = mnhd = mntl = 0;
    		for (int j = 1; j <= m; ++j) {
    			while (mxtl != mxhd && mx[mxtl-1] <= a[i][j]) mxtl--;
    			mx[mxtl] = a[i][j]; mxp[mxtl] = j; mxtl++;
    			if (mxp[mxhd] == j - k) mxhd++;
    			if (j >= k) mxa[i][j] = mx[mxhd];
    
    			while (mntl != mnhd && mn[mntl-1] >= a[i][j]) mntl--;
    			mn[mntl] = a[i][j]; mnp[mntl] = j; mntl++;
    			if (mnp[mnhd] == j - k) mnhd++;
    			if (j >= k) mna[i][j] = mn[mnhd];
    		}
    	}
    	int ans = 0x3f3f3f3f;
    	for (int j = k; j <= m; ++j) {
    		mxhd = mxtl = mnhd = mntl = 0;
    		for (int i = 1; i <= n; ++i) {
    			while (mxtl != mxhd && mx[mxtl-1] <= mxa[i][j]) mxtl--;
    			mx[mxtl] = mxa[i][j]; mxp[mxtl] = i; mxtl++;
    			if (mxp[mxhd] == i - k) mxhd++; // mark
    			if (i >= k) mxb[i][j] = mx[mxhd];
    
    			while (mntl != mnhd && mn[mntl-1] >= mna[i][j]) mntl--;
    			mn[mntl] = mna[i][j]; mnp[mntl] = i; mntl++;
    			if (mnp[mnhd] == i - k) mnhd++; // mark
    			if (i >= k) mnb[i][j] = mn[mnhd];
    		}
    		for (int i = k; i <= n; ++i) {
    			ans = std::min(ans, mxb[i][j] - mnb[i][j]);
    		}
    	}
    	printf("%d
    ", ans);
    	return 0;
    }
    

    Note

    mark那里将i错写成j,导致调了好久,还是要注意。

  • 相关阅读:
    UVa
    UVa 1630
    P3891 [GDOI2014]采集资源
    一个非常naive的小学数学魔术证明题
    P2831 [NOIP2016 提高组] 愤怒的小鸟
    P4211 [LNOI2014]LCA
    P4137 Rmq Problem / mex 强制在线做法
    P2272 [ZJOI2007]最大半连通子图
    P5664 [CSP-S2019] Emiya 家今天的饭
    盘点linux操作系统中的10条性能调优命令,一文搞懂Linux系统调优
  • 原文地址:https://www.cnblogs.com/wyxwyx/p/bzoj1047.html
Copyright © 2020-2023  润新知