首先肯定删 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;
}