[HAOI2007]修筑绿化带 题解

[HAOI2007]修筑绿化带 题解

题目地址:洛谷:【P2219】[HAOI2007]修筑绿化带 – 洛谷

题目描述

为了增添公园的景致,现在需要在公园中修筑一个花坛,同时在画坛四周修建一片绿化带,让花坛被绿化带围起来。
如果把公园看成一个M*N的矩形,那么花坛可以看成一个C*D的矩形,绿化带和花坛一起可以看成一个A*B的矩形。
如果将花园中的每一块土地的“肥沃度”定义为该块土地上每一个小块肥沃度之和,那么,
绿化带的肥沃度=A*B块的肥沃度-C*D块的肥沃度
为了使得绿化带的生长得旺盛,我们希望绿化带的肥沃度最大。

题意简述

给一个M*N矩阵,要求找到一个大小为A*B的子矩阵和这个子矩阵内部一个大小为A*B的子矩阵(该子矩阵不得与外部矩阵边界有交集)使得矩阵和之差最大。

输入输出格式

输入格式:
第一行有6个正整数M,N,A,B,C,D
接下来一个M*N的数字矩阵,其中矩阵的第i行j列元素为一个整数Xij,表示该花园的第i行第j列的土地“肥沃度”。

输出格式:
一个正整数,表示绿化带的最大肥沃程度。

输入输出样例

输入样例#1:

4 5 4 4 2 2
20 19 18 17 16
15 14 13 12 11
10 9 8 7 6
5 4 3 2 1

输出样例#1:

132

说明

数据范围
30%的数据,1<=M,N<=50
100%的数据,1<=M,N<=1000,1<=A<=M,1<=B<=N,1<=C<=A-2,1<=D<=B-2,1<=“肥沃度”<=100

题解

参考资料:[HAOI2007] 修筑绿化带 – CSDN博客
我们可以先处理出每一个宽度为C的行中,每一个右端点i对应的区间[i-B+1, i]中的最小C*D矩阵。这个处理极值显然可以用单调队列实现O(n^2)的处理。
接下来的思路,就是利用我们上面准备好的这个信息来求答案,我们枚举每一列,利用单调队列和上面预处理的信息,我们可以计算出以枚举到的当前行以上不超过A行的C*D矩阵的最小值。也就是说,我们现在正在竖着枚举A*B矩阵,并用单调队列来取出矩阵内的C*D子矩阵的最小值,最后算一波差值更新答案即可。这里的复杂度也是O(n^2),因此整体复杂度O(n^2)
比较麻烦的单调队列套路题。

代码

// Code by KSkun, 2018/5
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    register char c = fgc();
    while(!isdigit(c)) {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(isdigit(c)) {
        res = (res << 1) + (res << 3) + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 1005;

int n, m, a, b, c, d, sum[MAXN][MAXN], mn[MAXN][MAXN];

inline int queryab(int x, int y) {
    return sum[x][y] - sum[x - a][y] - sum[x][y - b] + sum[x - a][y - b];
}

inline int querycd(int x, int y) {
    return sum[x][y] - sum[x - c][y] - sum[x][y - d] + sum[x - c][y - d];
}

int que[MAXN], ql, qr;

int main() {
    n = readint(); m = readint();
    a = readint(); b = readint();
    c = readint(); d = readint();
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            sum[i][j] = readint() + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
        }
    }
    memset(mn, 0x3f, sizeof(mn));
    for(int i = c; i <= n; i++) {
        ql = qr = 0;
        for(int j = d; j <= m; j++) {
            while(ql < qr && que[ql] - d + 1 <= j - b + 1) ql++;
            if(ql < qr) {
                mn[i][j] = querycd(i, que[ql]);
            }
            while(ql < qr && querycd(i, que[qr - 1]) >= querycd(i, j)) qr--;
            que[qr++] = j;
        }
    }
    int ans = 0;
    for(int i = d; i <= m; i++) {
        ql = qr = 0;
        for(int j = c; j <= n; j++) {
            while(ql < qr && que[ql] - c + 1 <= j - a + 1) ql++;
            if(ql < qr && j >= a && i >= b) {
                ans = std::max(ans, queryab(j, i) - mn[que[ql]][i]);
            }
            while(ql < qr && mn[que[qr - 1]][i] >= mn[j][i]) qr--;
            que[qr++] = j;
        }
    }
    printf("%d", ans);
    return 0;
}


发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据