单调队列学习笔记

来源:互联网 发布:上海网络推广 编辑:程序博客网 时间:2024/06/07 13:45

单调队列学习笔记 —— 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;}