EZOJ #88

传送门

分析

自然想到二分

我们二分一个长度,之后考虑如何线性判断是否合法

我们可以维护一个单调队列表示从i开始的长度为d的区间和的最大值

每次用一段区间和减去它包含的长度为d的区间最大值即可

但是我们发现这个数据范围有那么一点点会t的征兆

于是我们考虑消除掉二分的log

于是我们继续用单调队列维护,用双指针扫一下就可以了

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
inline int ra(){
    int x=0,f=1;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s))x=(x<<3)+(x<<1)+(s-'0'),s=getchar();
    return x*f;
}
inline long long ra2(){
    long long x=0,f=1;char s=getchar();
    while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
    while(isdigit(s))x=(x<<3)+(x<<1)+(s-'0'),s=getchar();
    return x*f;
}
long long pre[1000100],del[1000100],a[1000100],q[1000100],m;
int id[1000100],fi,tl;
int n,d;
int main(){
    int i,j=1,k;
    n=ra(),m=ra2(),d=ra();
    for(i=1;i<=n;i++)a[i]=ra2();
    for(i=1;i<=n;i++)pre[i]=pre[i-1]+a[i];
    for(i=1;i<=n-d+1;i++)del[i]=pre[i+d-1]-pre[i-1];
    int Ans=0;
    fi=1,tl=1;
    q[tl]=del[1];
    for(i=d;i<=n;i++){
      while(tl>=fi&&q[tl]<=del[i-d+1])tl--;
      q[++tl]=del[i-d+1];
      id[tl]=i-d+1;
      for(j;j<=i;j++){
        while(id[fi]<j)fi++;
        long long x=q[fi];
        if(pre[i]-pre[j-1]-x<=m){
          Ans=max(Ans,i-j+1);
          break;
        }
      }
    }
    printf("%d
",Ans);
    return 0;
}
原文地址:https://www.cnblogs.com/yzxverygood/p/9907456.html