You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

76 lines
3.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <bits/stdc++.h>
using namespace std;
const int N = 500010;
typedef long long LL;//五年OI一场空不开LONG LONG
int n, d, k; // n:格子数d:机器人可以跳的距离,k:想要取得的分值
int x[N], w[N]; // 距离数组x[i]表示i号格子距离出发点的距离w[i]从i号格子中可以获得的分值
LL f[N]; // f[i]:到达i点时可以获取到的最大分值
int q[N]; // 单调队列,记录的是格子号
bool check(int g) { // g 描述的是给定的金币数量
LL res = 0;
// 多次dp,每次需要清空统计数组
memset(f, -0x3f, sizeof f); // 预求最大,先设最小
f[0] = 0; // 递推起点数据分值为0,这个要先看状态转移方程,再思考整体初始化、起点初始化
int hh = 0, tt = -1;
// L,R : 左右边界
int L = max(1, d - g), R = d + g;
/*
④ 为什么要先处理入队列,再处理出队列?反过来为什么是错的呢?
我们向单调队列中添加节点的逻辑是发现x[i]-x[k]>=L,也就是k没有进过队列没有参加
评比过并且两个格子间的距离大于了L就让它进来试试。
但是这样就真的可以了吗因为我们知道单调队列中保存是一个个格子号而这些格子号必须在i左侧指定的范围内才行。
x[i]-x[k]>=L是成立了那会不会k离i太远了超过了上限R呢如果是这样的话那么k是不能停留在队列中的需要去除掉。
*/
for (int i = 1, j = 0, k = 0; i <= n; i++) {
// ① 新元素入队列
while (x[i] - x[k] >= L) { // i走的挺快k有资格参评了
while (hh <= tt && f[q[tt]] <= f[k]) tt--; // 年龄比k大值比k小的都去死
q[++tt] = k++; // k入队列
}
// ② 越界元素出队列
while (x[i] - x[j] > R) j++; // j出界,j无法跳到i
while (hh <= tt && q[hh] < j) hh++; // j都出界了单调队列也需要维护把比j小的都干掉
// ③ 队列非空,队列头中保存的就是前面[L,R]范围内的分数最大值所对应的y号格子
if (hh <= tt) f[i] = f[q[hh]] + w[i]; // 再加上i号格子中的w[i]分值,就是总分值
// 时刻更新最大值
res = max(res, f[i]);
}
// 因为分值中存在负数一旦是在半途中出现可以获取到的分值大于等于k时就可以停止掉表示已经找到了一个金币数量满足增加了自由度后可以取得k这样的分值
return res >= k;
}
int main() {
// 加快读入
ios::sync_with_stdio(false), cin.tie(0);
cin >> n >> d >> k; // 格子数量,机器人每次可以跳的距离,想要拿到的分数
for (int i = 1; i <= n; i++) cin >> x[i] >> w[i]; // 起点到第i个格子的距离,第i个格子的分数
int L = 0, R = 1e9, ans = -1; // 二分的左右无脑边界值
/*
二分的最后结果有两种方式:
1、使用ans变量记录最终结果ans初始化为-1,每次找到check()通过的mid,ans=mid,这样如果最终有答案ans就记录的是答案否则就记录的是-1。
2、不使用ans变量记录而是最后再用!check(L)然后输出-1。
个人认为方法1更易理解。
*/
while (L < R) {
int mid = L + R >> 1;
if (check(mid)) {
R = mid;
ans = mid;
} else
L = mid + 1;
}
printf("%d\n", ans);
return 0;
}