2019年牛客多校第三场 F题Planting Trees(单调队列)

题目链接

传送门

题意

给你一个\(n\times n\)的矩形,要你求出一个面积最大的矩形使得这个矩形内的最大值减最小值小于等于\(M\)

思路

单调队列滚动窗口。

比赛的时候我的想法是先枚举长度然后再枚举左端点,最后用单调队列来做,然后\(T\)成傻逼,赛后看大佬们的代码发现我的做法比先枚举左端点再枚举右端点多很多状态(譬如在每次长度变化的时候我的写法会从最左边开始枚举,而先枚举左端点的做法则不会再反复考虑左边的情况)。

我们先固定左端点,然后在枚举右端点的时候用两个数组记录\([L,R]\)中每一行的最大值、最小值,然后用单调队列来维护矩形中最大值减最小值小于等于\(M\)的面积,这也算是一个板子题了,比赛的时候没过还是因为思维僵化\(+\)刷题太少啊。

代码实现如下

#include <set>
#include <map>
#include <deque>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;

typedef long long LL;
typedef pair<LL, LL> pLL;
typedef pair<LL, int> pLi;
typedef pair<int, LL> pil;;
typedef pair<int, int> pii;
typedef unsigned long long uLL;

#define lson rt<<1
#define rson rt<<1|1
#define lowbit(x) x&(-x)
#define name2str(name) (#name)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["<<x<<"]" <<endl
#define FIN freopen("D://Code//in.txt","r",stdin)
#define IO ios::sync_with_stdio(false),cin.tie(0)

const double eps = 1e-8;
const int mod = 1000000007;
const int maxn = 1000000 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;

int t, n, M;
int bk1, bk2, tp1, tp2;
int q1[2007], q2[2007];
int mp[505][505], num1[505], num2[505];

int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &M);
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= n; ++j) {
                scanf("%d", &mp[i][j]);
            }
        }
        int ans = 0, tmp;
        for(int L = 1; L <= n; ++L) {
            for(int i = 1; i <= n; ++i) num1[i] = -inf, num2[i] = inf;
            for(int R = L; R <= n; ++R) {
                for(int i = 1; i <= n; ++i) num1[i] = max(num1[i], mp[i][R]), num2[i] = min(num2[i], mp[i][R]);
                int tmp = 0;
                bk1 = bk2 = tp1 = tp2 = 1000;
                for(int i = 1; i <= n; ++i) {
                    while(bk1 != tp1 && num1[i] >= num1[q1[tp1]]) --tp1;
                    while(bk2 != tp2 && num2[i] <= num2[q2[tp2]]) --tp2;
                    q1[++tp1] = i;
                    q2[++tp2] = i;
                    while(bk1 != tp1 && bk2 != tp2 && num1[q1[bk1+1]] - num2[q2[bk2+1]] > M) {
                        if(q1[bk1+1] > q2[bk2+1]) {
                            tmp = q2[bk2+1];
                            ++bk2;
                        } else {
                            tmp = q1[bk1+1];
                            ++bk1;
                        }
                    }
                    ans = max(ans, (R - L + 1) * (i - tmp));
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

\(ps.STL\)里面的\(deque\)的常数巨大,以后尽量还是手写队列!

posted @ 2019-07-25 21:25  Dillonh  阅读(530)  评论(0编辑  收藏  举报