BZOJ4385 POI2015 Wilcze doły

来源:互联网 发布:2015年网络小说家排名 编辑:程序博客网 时间:2024/05/16 10:56

Description

给定一个长度为n的序列,你有一次机会选中一段连续的长度不超过d的区间,将里面所有数字全部修改为0。

请找到最长的一段连续区间,使得该区间内所有数字之和不超过p。

Input

第一行包含三个整数n,p,d(1<=d<=n<=2×106,0<=p<=1016)。

第二行包含n个正整数,依次表示序列中每个数wi(1<=wi<=109)。

Output

包含一行一个正整数,即修改后能找到的最长的符合条件的区间的长度。

Sample Input

9 7 2
3 4 1 9 4 1 7 1 3

Sample Output

5


Solution:

再一次验证了尺取玄学这句话。

显然本题是在要求在你尺取的区间内再选择一个固定长度的小区间,使得大区间的权值和减去小区间的权值和始终小于给定值:

sum[L+len1]sum[L1]<=p+sum[tot1]sum[tot+d1]
我们固定大区间的左端点L,尺取挪动R,由于要求得最大值len,所以类似POI2010 Pilots的做法,不进行尺取中收缩的操作即可。而对于中间的小区间,由于长度固定,所以可以将其当成一个点,那么就需要用滑动窗口得到当前区间的长度为d的最值。

然后对于L,R,len三者的关系进行协调即可。预计调一年

#include <bits/stdc++.h>#define M 2000005using namespace std;inline void Rd(int &res){    res=0;char c;    while(c=getchar(),c<48);    do res=(res<<3)+(res<<1)+(c^48);    while(c=getchar(),c>47);}long long sum[M];int deq[M],L=0,R=-1;int main(){    int n,d;    long long p;    scanf("%d %lld %d",&n,&p,&d);    for(int i=1;i<=n;i++){        int val;Rd(val);        sum[i]=sum[i-1]+val;    }    long long len=d;    deq[++R]=d;    for(int i=1;i<=n;i++){        while(L<=R&&deq[L]-d+1<i)++L;        //Big: sum[len+i-1]-sum[i-1],Small: sum[tot]-sum[tot-d]        while(len+i-1<n){            ++len;            long long big=sum[len+i-1]-sum[i-1];            while(L<=R&&sum[deq[R]]-sum[deq[R]-d]<sum[len+i-1]-sum[len+i-1-d])--R;            deq[++R]=len+i-1;            if(big-(sum[deq[L]]-sum[deq[L]-d])>p){--len;break;}        }        if(len+i-1==n){            printf("%d\n",(int)len);            return 0;        }    }    printf("%d\n",len);    return 0;}
0 0