(辅助队列 M 即为单调队列
NOIP2017普及组★跳房子
显然答案有单调性,所以二分答案。
判断时 DP。
f[i]
表示跳前i个格子,且停在第 i 个格子最大分数;
score[i]
表示第 i 个格子的分数。
易得转移方程
f[i]=max(f[j]|从j可以跳到i)+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;
}