题目大意:给我们一个序列,要求我们选择一段长度不小于f的连续区间,求合法区间中每个格子的最大平均值。
数据范围1e5,n2的算法无法完成。考虑二分答案,如何判断答案满不满足要求,即判断是否存在一个长度不小于f的连续区间,它的平均值大于等于二分出的答案。所以现在的问题转变成了如何写check函数。
如果我们不做任何处理,对于每个区间,我们既要求区间和,又要求区间长度,这很不好去维护。但是如果我们让所有数都减去一个平均值,我们只需要找一个长度不小于f的且区间和大于0的连续区间即可,我们只需要维护一个区间和就行了。假设b是原序列减去平均值后的序列,我们对他求前缀和s,枚举区间右端点i,在1~i-f+1中找一个左端点,使得s[i]-s[j-1]>=0。其实我们只需要在枚举右端点的过程中,依次更新1~i-f+1的最小值minn,判断s[i]-minn是否大于0。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
const double eps=1e-8;
double a[N],b[N],sum[N];
int n,f;
bool check(double x){
double minn=0;
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i]-x;
for(int i=f,j=0;i<=n;i++,j++){
minn=min(minn,sum[j]);
if(sum[i]-minn>=0)return true;
}
return false;
}
int main(){
cin>>n>>f;
for(int i=1;i<=n;i++)cin>>a[i];
double l=0,r=2000;
while(fabs(r-l)>eps){
double mid=(l+r)/2;
if(check(mid))l=mid;
else r=mid;
}
printf("%d\n",(int)(r*1000));
return 0;
}