题目链接:https://ac.nowcoder.com/acm/contest/153/1004
题意
题解
首先知道如果所有的正数加起来小于k那么就输出-1,否则肯定有解。且解的大小在[1,x[n]-d]之间。这里可以二分答案。
对答案的判定用到了dp,dp[i]:前i格的最大分值,转移方程为:
dp[i] = max(dp[j]+s[i]), j < i ,同时满足j要在能跳的范围之内。因为n的范围是500000,所以二维dp肯定超时,这里找移动区间的最大值可以用单调队列,这样一次判断总的只要扫一遍就可以得出答案。
单调队列: 队头是当前区间的最大值
更新:对于要进队的元素,从队尾开始把小于进队元素的值都pop出来,然后在push进元素。然后要把队头超过可跳距离的元素pop出来。
这里要注意的是,进队的话只要距离大于跳跃的最小距离即可,不用小于最大距离,否则的话会导致后续元素无法进队。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+5;
const ll INF = 1LL<<60;
ll x[maxn],s[maxn];
int n,d,k;
ll sum;
long long dp[maxn];
bool ok(int g) {
int d1 = d, d2=d;
if(g < d) {
d1 = d-g; d2 = d+g;
}else {
d1 = 1; d2 = d+g;
}
deque<int> que;
int cur = 0;
dp[0] = 0;
for(int i = 1; i <= n; ++i) {
for(;cur < i && x[i]-x[cur]>=d1; ++cur) {
if(que.empty())
que.push_back(cur);
else {
while(!que.empty() && dp[que.back()] <= dp[cur])
que.pop_back();
que.push_back(cur);
}
}
while(!que.empty() && x[i]-x[que.front()] > d2)
que.pop_front();
if(que.empty())
dp[i] = -INF;
else {
dp[i] = dp[que.front()]+s[i];
}
if(dp[i] >= k)
return true;
}
return false;
}
int main() {
scanf("%d%d%d", &n, &d,&k);
x[0] = s[0] = 0;
for(int i = 1; i <= n; ++i) {
scanf("%lld%lld",&x[i], &s[i]);
if(s[i] > 0) {
// suf[i] = s[i];
sum += s[i];
}
}
if(sum < k) {
puts("-1");
exit(0);
}
int r = x[n]-d;
int l = 0,ans = -1;
while(l <= r) {
int mid = (l+r)>>1;
if(ok(mid)) {
ans = mid;
r = mid-1;
}else
l = mid+1;
}
cout << ans << endl;
return 0;
}