BZOJ4385: [POI2015]Wilcze doły
$n \leq 2000000$的正数列,有一次机会把一段长度不超过$d$的数变成0,问最长的和不超过$p$的序列。
选的$d$区间一定是答案区间$[L,R]$的最大字段和。可以证明$R$往右时$L$不会往左。反证一下,假如出现了这种情况:
其中蓝色和绿色表示选中的$d$区间(绿不可能在R1左边,否则要么与蓝是最大子段和相悖,要么$L_1$还能再往左延伸到$L_2$)。然后,把这两段切一下:
左边黄色那段两边一样多,右边黄色那段上面要比下面小(去掉蓝绿后),因为蓝色那段是这一小黄段的最大$d$子段和,绿色那段又不到$d$又不是最大,所以剩下那一部分肯定下面的大。既然如此,那$L_1$延伸到$L_2$处都是没有问题的,得证。
知道这个事情之后,就双指针扫下,至于最大子段和,可以搞一个值递减下标递增的单调队列。
1 //#include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 //#include<math.h> 5 //#include<set> 6 //#include<queue> 7 //#include<bitset> 8 //#include<vector> 9 #include<algorithm> 10 #include<stdlib.h> 11 using namespace std; 12 13 #define LL long long 14 int qread() 15 { 16 char c; int s=0,f=1; while ((c=getchar())<'0' || c>'9') (c=='-') && (f=-1); 17 do s=s*10+c-'0'; while ((c=getchar())>='0' && c<='9'); return s*f; 18 } 19 20 //Pay attention to '-' , LL and double of qread!!!! 21 22 int n,D; LL m; 23 #define maxn 2000011 24 int a[maxn]; LL sum[maxn]; 25 int que[maxn],head=1,tail=1; 26 27 int main() 28 { 29 n=qread(); scanf("%lld",&m); D=qread(); 30 for (int i=1;i<=n;i++) a[i]=qread(),sum[i]=sum[i-1]+a[i]; 31 32 int L=1,R=D,ans=D; LL tot=sum[D]; que[tail++]=D; 33 for (R++;R<=n;R++) 34 { 35 while (head<tail && sum[R]-sum[R-D]>sum[que[tail-1]]-sum[que[tail-1]-D]) tail--; 36 que[tail++]=R; 37 tot+=a[R]; 38 while (tot-(sum[que[head]]-sum[que[head]-D])>m) 39 { 40 tot-=a[L]; 41 while (que[head]<L+D) head++; 42 L++; 43 } 44 ans=max(ans,R-L+1); 45 } 46 printf("%d\n",ans); 47 return 0; 48 }