[HAOI2007] 修筑绿化带

类型:单调队列

传送门:>Here<

题意:给出一个$M*N$的矩阵,每一个代表这一格土地的肥沃程度。现在要求修建一个$C*D$的矩形花坛,矩形绿化带的面积为$A*B$,要求花坛被包裹在绿化带中,且不能碰到绿化带边缘。问绿化带的最大肥沃程度

解题思路

暴力做法:枚举绿化带,然后选出能使其肥沃程度最大的花坛位置。

很容易发现要使绿化带的肥沃程度最大,也就是让所选的花坛的肥沃程度尽量小。由此,问题转化为了求固定矩形内部规定大小的最小子矩形

为了表达方便,以下称绿化带为矩形$AB$,花坛为矩形$CD$。

为了计算方便,我们可以先预处理出以$(i,j)$为右下角的矩形$AB$和矩形$CD$的肥沃程度。利用前缀和处理即可

显然固定矩形$AB$以后,矩形$CD$的可取范围也被固定了。这个范围是可以计算的。设矩形$AB$的右下角为$(i+1,j+1)$,则矩形$CD$的右下角可取范围是$(i+1-(A-1)+C \rightarrow i, j+1-(B-1)+D \rightarrow j)$。也就是$$(i-A+C+2 \rightarrow i, j-B+D+2 \rightarrow j)$$由于我们已经计算出了以$(i,j)$为右下角的矩形$CD$的肥沃程度,因此我们可以把每一个格子看做是一个矩形$CD$。于是要求以同一个$i$作为右下角横坐标的矩形时,他们就是一个横行里的格子。用一个单调队列就很容易求出

求出范围内每一个横行的最小值以后,对所有这些最小值再竖着用单调队列求一个最小值。即可以得出整个范围内的最小值。问题就解决了

这题的代码实现也很难(本蒟蒻打了2.5h+),由于边界条件如此的坑而且样例又如此的水,不得不自己造了好几组数据一个一个调……关键在于把控清楚每一个数组表示的具体内容,包括要不要$+1或-1$

Code

/*By DennyQi 2018.8.19*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define  r  read()
#define  Max(a,b)  (((a)>(b)) ? (a) : (b))
#define  Min(a,b)  (((a)<(b)) ? (a) : (b))
using namespace std;
typedef long long ll;
const int MAXN = 1010;
const int INF = 1061109567;
inline int read(){
    int x = 0; int w = 1; register int c = getchar();
    while(c ^ ‘-‘ && (c < ‘0‘ || c > ‘9‘)) c = getchar();
    if(c == ‘-‘) w = -1, c = getchar();
    while(c >= ‘0‘ && c <= ‘9‘) x = (x<<3) + (x<<1) + c - ‘0‘, c = getchar();
    return x * w;
}
int M,N,A,B,C,D,h,t;
int a[MAXN][MAXN],sum[MAXN][MAXN],ab[MAXN][MAXN],cd[MAXN][MAXN];
int q[MAXN],mcd[MAXN][MAXN],mx[MAXN][MAXN];
int main(){
    N = r, M = r;
    A = r, B = r, C = r, D = r;
    for(int i = 1; i <= N; ++i){
        h = 1, t = 0;
        for(int j = 1; j <= M; ++j){
            a[i][j] = r;
            sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + a[i][j];
            ab[i][j] = sum[i][j] - sum[i][j-B] - sum[i-A][j] + sum[i-A][j-B];
            cd[i][j] = sum[i][j] - sum[i][j-D] - sum[i-C][j] + sum[i-C][j-D];
            if(i > C && j > D && i < N && j < M){
                while(h <= t && cd[i][q[t]] >= cd[i][j]) --t;
                q[++t] = j;
                if(j - B + D + 2 > 0){
                    while(h <= t && q[h] < j - B + D + 1) ++h;
                }
                if(i > C && j > D) mcd[i][j] = cd[i][q[h]];
            }
        }
    }
    for(int k = B; k <= M; ++k){
        h = 1, t = 0;
        q[0] = 0;
        for(int i = C+1; i <= N; ++i){
            while(h <= t && q[h] < i - A + C + 1) ++h;
            if(i >= A) mx[i][k] = mcd[q[h]][k-1];
            while(h <= t && mcd[q[t]][k-1] >= mcd[i][k-1]) --t;
            q[++t] = i;
        }
    }
    int ans = 0;
    for(int i = A; i <= N; ++i){
        for(int j = B; j <= M; ++j){
            ans = Max(ans, ab[i][j] - mx[i][j]);
        }
    }
    printf("%d", ans);
    return 0;
}

原文地址:https://www.cnblogs.com/qixingzhi/p/9501119.html

时间: 2024-10-04 22:47:45

[HAOI2007] 修筑绿化带的相关文章

P2219 [HAOI2007]修筑绿化带

题目描述 为了增添公园的景致,现在需要在公园中修筑一个花坛,同时在画坛四周修建一片绿化带,让花坛被绿化带围起来. 如果把公园看成一个M * N的矩形,那么花坛可以看成一个C * D的矩形,绿化带和花坛一起可以看成一个A * B的矩形. 如果将花园中的每一块土地的"肥沃度"定义为该块土地上每一个小块肥沃度之和,那么, 绿化带的肥沃度=A * B块的肥沃度-C * D块的肥沃度 为了使得绿化带的生长得旺盛,我们希望绿化带的肥沃度最大. 输入输出格式 输入格式: 第一行有6个正整数M,N,A

洛谷.2219.[HAOI2007]修筑绿化带(单调队列)

题目链接 洛谷 COGS.24 对于大的矩阵可以枚举:对于小的矩阵,需要在满足条件的区域求一个矩形和的最小值 预处理S2[i][j]表示以(i,j)为右下角的C\(*\)D的矩阵和, 然后对于求矩形区域的最小值,可以先将每行看做一个数列,对于每个点y,得到一个[y-(B-3),y]的最小值 处理完行后得到Minr[][],对每列的进行同样的操作,就可以得到Min[x][y]表示([x-A+3,x],[y-B+3,y])的最小矩形和 但是注意单调队列处理的是S2,S2表示的是C\(*\)D的和,n

[题解] [HAOI2007] 修筑绿化带

题面 题解 题目意思比较简单, 就不在这里赘述了 本着练习平衡树的思路, 我把方法尝试往上面去套, 结果想不出 只能弃掉平衡树 最后想出来的方法是这样的 我们运用类似于高维前缀和那样一维一维加上去的方法 先横着统计在某个范围内和最小的 \(c * d\) 矩阵, 把贡献算在范围的右下角 注意, 这里保证了这个矩阵在以 \((i, j)\) 为右下角的某个范围之内 然后再竖着计算答案, 我们固定 \(a * b\) 矩形的右边在某一列, 就只用考虑行的变化了 从上往下依次枚举右下角, 每次弹出不合

修筑绿化带题解

修筑绿化带题解 我的做法实际上神奇且麻烦,大家其实可以看一下别人的做法. 这是一个单调队列优化DP题(废话). 我的大概做法是:将每个c乘d的矩形化成一个点,将从a乘b的矩形中选一个c乘d的矩形转化为:在(a-c+1)*(b-d+1)的矩形中选一个值最小的点. 具体做法如下: 1.求出二维前缀和p,再求出以一个点为左上角的c乘d的矩形的值之和a. 2.求出每一以某点开始的长度为a-c+1的列的最小值sum. 3.剩下的就是在n-a+1行上,每一行求出长度为b-d+1的sum最小值(实际上就是每个

单调队列单调栈

单调队列单调栈 Tags:数据结构 更好阅读体验:https://www.zybuluo.com/xzyxzy/note/1041449 一.概述 单调队列单调栈是很基础的数据结构,常用来优化一些东西比如说优化DP 那么大概意思就是在栈或队列中按照某关键字单调维护信息,从而实现一些功能 其实很久之前接触过单调队列和单调栈,但一直没有刷题,趁这两天被LCT弄晕的时候复习下这些 先看题 二.题单 普及- [x] P1886 滑动窗口 https://www.luogu.org/problemnew/

BZOJ 1046: [HAOI2007]上升序列 LIS -dp

1046: [HAOI2007]上升序列 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3438  Solved: 1171[Submit][Status][Discuss] Description 对于一个给定的S={a1,a2,a3,…,an},若有P={ax1,ax2,ax3,…,axm},满足(x1 < x2 < … < xm)且( ax1 < ax2 < … < axm).那么就称P为S的一个上升序列.如果有多

【BZOJ】【1046】【HAOI2007】上升序列

DP+贪心 啊……其实是个水题,想的复杂了 令f[i]表示以 i 为起始位置的最长上升子序列的长度,那么对于一个询问x,我们可以贪心地从前往后扫,如果f[i]>=x && a[i]>last,则x--,last=a[i] 保证$x_i$(下标)字典序最小…… 1 /************************************************************** 2 Problem: 1046 3 User: Tunix 4 Language: C++

[BZOJ1047][HAOI2007]理想的正方形

1047: [HAOI2007]理想的正方形 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 3481  Solved: 1917 [Submit][Status][Discuss] Description 有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值 的差最小. Input 第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数.每 行相邻

【BZOJ 1053】 1053: [HAOI2007]反素数ant (反素数)

1053: [HAOI2007]反素数ant Description 对于任何正整数x,其约数的个数记作g(x).例如g(1)=1.g(6)=4.如果某个正整数x满足:g(x)>g(i) 0<i<x ,则称x为反质数.例如,整数1,2,4,6等都是反质数.现在给定一个数N,你能求出不超过N的最大的反质数么 ? Input 一个数N(1<=N<=2,000,000,000). Output 不超过N的最大的反质数. Sample Input 1000 Sample Output