BZOJ4385: [POI2015]Wilcze doły
首先肯定删 d 个是最优的
假设当前选出的区间为 [l,r] ,删去的一定是和最大的子串
把每个位置的值换成从当前位置向前长度d的区间的和
删的一定是最大的位置
随着区间右端点的移动,左端点是单调不降的
可以维护两个指针,再找一个东西求区间最大值就行了
由于区间端点都是单调的,所以可以单调队列做,
枚举右端点维护左端点,每次取队头作为删掉的部分
当删掉最大的区间后和还是大于给定值时,就移动左端点
如何检查队头的合法性?
由于最开始显然的性质,每次删去一段长度为 d 的区间
那么如果 队头位置 - d < l 了就不符合前边的做法了,
在这样的不合法的前提下如果每次删去的区间和左端点取 max 的话在之前是没有问题的
等到左端点超过队头就该 GG 了
代码:
#include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <cctype> #include <cstdio> using namespace std; typedef long long ll; const int MAX_N = 2000005; int n, d, hd, tl, ans; ll p; int num[MAX_N]; ll pre_sum[MAX_N], sig_d[MAX_N]; int q[MAX_N]; inline int rd() { register int x = 0, c = getchar(); while (!isdigit(c)) c = getchar(); while (isdigit(c)) { x = x * 10 + (c ^ 48); c = getchar(); } return x; } inline ll rd_ll() { register ll x = 0; register int c = getchar(); while (!isdigit(c)) c = getchar(); while (isdigit(c)) { x = x * 10ll + (c ^ 48); c = getchar(); } return x; } int main() { n = rd(); p = rd_ll(); d = rd(); for (int i = 1; i <= n; ++i) { num[i] = rd(); pre_sum[i] = pre_sum[i - 1] + num[i]; sig_d[i] = pre_sum[i] - pre_sum[(i > d) ? (i - d) : 0]; } ans = d; register int bgn = 1; tl = 1; q[++hd] = d; for (int i = d + 1; i <= n; ++i) { while (hd <= tl && sig_d[i] >= sig_d[q[tl]]) --tl; q[++tl] = i; while (hd <= tl && pre_sum[i] - pre_sum[bgn - 1] - sig_d[q[hd]] > p) { ++bgn; while (hd <= tl && q[hd] - d + 1 < bgn) ++hd; } ans = max(ans, i - bgn + 1); } printf("%d\n", ans); return 0; }
禁止诸如开发者知识库/布布扣/码迷/学步园/马开东等 copy 他人博文乃至博客的网站转载
,用户转载请注明出处:https://www.cnblogs.com/xcysblog/