Files
python/TangDou/CSP-J/AcWingTraining/T7/472_1.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

76 lines
3.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}