2023秋季专题训练五(二分)F

发布时间 2023-12-21 23:11:36作者: 不o凡

问题 K: 计算平均值最大子段

可以想到的做法是先枚举区间长度,然后计算每一个符合的区间平均值,但是会超时(timeout),很明显是时间复杂度n^2

考虑如何优化(当然一开始没想到,还是老师提醒了一波)(明明之前课上还做到过)(哭)

如何在O(n)判断一个区间是否满足,除了前缀和加除法的方法,也可以将数组的所有元素减去平均值再做前缀和,这样如果区间大于等于0那么就是满足的,相当于式子转化一下((a+b+c)/len=avg => a+b+c=len*avg)

这样我们就可以贪心的想,最大值-最小值>=0,且保证两则距离大于等于L,这样即可在O(n)下完成

下面是实现过程:
先让区间[l,r](保证r-l>=L)右移,维护[0,l-1]的最小值,对于每个r,如果pre[r]-mn>=0则满足

点击查看代码
int n,L;
double a[N],pre[N];

bool check(double x){
    for(int i=1;i<=n;i++) pre[i]=pre[i-1]+a[i]-x;//将求平均值直接转化为前缀和
    double  mn=1e9;
    for(int i=L,j=0;i<=n;i++,j++){
        mn=min(mn,pre[j]);//取区间外前缀的最小值
        if(pre[i]-mn>=0) return true;//枚举每个符合的i
    }
    return false;
}

void bu_f(){
    n=read(),L=read();
    for(int i=1;i<=n;i++) cin>>a[i];
    double l=0,r=3000;
    while(r-l>1e-5){
        double mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }
    cout<<(int)(r*1000);//注意输出,向上取整直接取r即可
}