跳飞机♂

又是我,臭不要脸的更了一道水题

 

 

 

 题目来源还是jsoi。。。

不多说,这道题一眼二分+dp,关键是n值很大 (n<=5e5

然而最容易想到的是dp[i]表示当前跳到i时能取到的最大数字和。

得出方程dp[i]=max(dp[0~(i-1)])+s[i] (其中必须满足那个距离条件,i点到某点的距离>=max(1,d-g),<=d+g) 因为实在太菜写不出数学表达式。。。

可是二分要用掉O(lgn),检查函数里复杂度只能是O(n),此时考虑优化。

发现一个点,如果i点与某点距离》d+g,i+1肯定也不考虑。

此时考虑双指针 (?)但是在两个指针范围内找最大值依然是O(n)的,沿着思路继续优化

用单调队列,使用STL deque容器

让这个容器中的点照着dp的值单调递减,且保证这个容器里面的点都是能够跨到i点

那么dp【i】只需要从dp【dq.front()]更新过来即可

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 10;
const long long uinf = -0x3f3f3f3f3f3f;
int n, d, k;
long long s[maxn], x[maxn];
long long dp[maxn];
bool check(int g) {
  memset(dp, 0, sizeof(dp));
  int p = 0;
  int lpos = max(1, d - g), rpos = d + g;
  deque<int> dq;
  for (int i = 1;i <= n;++i) {
    for (;p < i && x[i] - x[p] >= lpos;++p) {
      while (!dq.empty() && dp[p] >= dp[dq.back()]) {
        dq.pop_back();
      } //每次从尾部往前清除比它小的,然后把他塞进去,那么这样一定能保证dp[p]在dq里面是最小的
      dq.push_back(p);
    }
    while (!dq.empty() && x[i] - x[dq.front()] > rpos) {
      dq.pop_front();
    } // 砍掉距离过大的点
    if (dq.empty() == 1) {
      dp[i] = uinf;
    }//dq为空那就说明没有点可以用来更新
    else {
      dp[i] = dp[dq.back()] + s[i]; //故意写错防止<c-c>大法 , 应从dp[dq.front]更新 想来想去还是应该加上来
    }
    if (dp[i] >= k) return 1;
  }
  return 0;
}
int main() {
  scanf("%d%d%d", &n, &d, &k);
  for (int i = 1;i <= n;++i) {
    scanf("%lld%lld", &x[i], &s[i]);
  }
  int l = 0, r = x[n], ans = 0;
  while (l <= r) { //二分模板不解释
    int mid = (l + r) >> 1;
    if (check(mid)) {
      r = mid - 1;
      ans = mid;
    }
    else {
      l = mid + 1;
    }
  }
  printf("%d", ans);
}

//最近有点颓废

 

 

posted @ 2021-02-08 21:28  周昂没有周昂  阅读(53)  评论(0)    收藏  举报