单调队列学习笔记

单调队列学习笔记 —— By Menci

(辅助队列 M 即为单调队列


Luogu P3957 跳房子

NOIP2017普及组★跳房子
显然答案有单调性,所以二分答案。
判断时 DP。
f[i] 表示跳前i个格子,且停在第 i 个格子最大分数;
score[i] 表示第 i 个格子的分数。
易得转移方程 f[i]=max(f[j]|ji)+score[i]
时间复杂度: Θ(log2NN2)
考虑优化

为了防止被卡手写 deque
这里的 nextPos 即为枚举的格子 j

_ _ _ _ _ _ _ _ _ nextPos i
(单调队列维护)

#include <bits/stdc++.h>

using namespace std;
const int N = 5e5 + 5;

int n, d, k;
int pos[N], score[N];

inline bool in(int x, int l, int r) {
    return x >= l && x <= r;
}

inline bool check(int limit) {
    int xl = max(1, d - limit), xr = d + limit;

    static int q[N]; // q 是维护的单调队列 
    int *ql = q, *qr = q - 1; // ql 是 q 当前的最大值,qr 是 q 的最小值 

    static int f[N];
    int nextPos = 0; // nextPos 是尝试在 qr 右边新加入的数的位置 
    int ans = 0;

    for(int i = 1; i <= n; i ++) {
        // 左边不满足的删掉
        while (ql <= qr && !in(pos[i] - pos[*ql], xl, xr)) ql ++;

        // 尝试右边能不能加
        for (; nextPos <= n && pos[i] - pos[nextPos] > xr; nextPos ++);
        for (; nextPos <= n && pos[i] - pos[nextPos] >= xl; nextPos ++) {
            // 将 nextPos 加入到单调队列
            // 维护单调性
            while(ql <= qr && f[*qr] <= f[nextPos]) qr --; // nextPos 与队尾元素 qr 比较 
            *++ qr = nextPos; // nextPos 成为新的队尾 
        }

        if (ql <= qr && f[*ql] != INT_MIN) // 为了避免下一句 INT_MIN + 一个负数溢出
            f[i] = f[*ql] + score[i];
        else f[i] = INT_MIN;

        ans = max(ans, f[i]);
    }

    return ans >= k;
}

int main() {
    scanf("%d%d%d", &n, &d, &k);

    for (int i = 1; i <= n; i ++) scanf("%d%d", &pos[i], &score[i]);

    int l = 0, r = pos[n];
    while (l < r) {
        int mid = l + (r - l) / 2;
        if (check(mid)) r = mid;
        else l = mid + 1; 
    }

    printf("%d\n", check(pos[n]) ? l : -1);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值