跳飞机♂
又是我,臭不要脸的更了一道水题
 
题目来源还是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); }
//最近有点颓废

                
            
        
浙公网安备 33010602011771号