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;
}
posted @ 2018-10-31 21:17  EvalonXing  阅读(147)  评论(0编辑  收藏  举报