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 3Sample Output
5
Solution:
再一次验证了尺取玄学这句话。
显然本题是在要求在你尺取的区间内再选择一个固定长度的小区间,使得大区间的权值和减去小区间的权值和始终小于给定值:
sum[L+len−1]−sum[L−1]<=p+sum[tot−1]−sum[tot+d−1]
我们固定大区间的左端点L,尺取挪动R,由于要求得最大值len,所以类似
POI2010 Pilots的做法,不进行尺取中收缩的操作即可。而对于中间的小区间,由于长度固定,所以可以将其当成一个点,那么就需要用
滑动窗口得到当前区间的长度为d的最值。
然后对于L,R,len三者的关系进行协调即可。预计调一年。
#include <bits/stdc++.h>
#define M 2000005
using 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;
}