ZROI2018普转提day1t1

传送门

分析

我们先二分一下最终的平均值mid,然后让序列中的每一个数都减去这个mid,之后用新序列的前缀和建一棵线段树,枚举起点i,然后求出此时在i+L-1~i+R-1范围内的前缀和的最大值,用这个数减去pre[i],这就是以i为起点的长度为L~R范围内的序列最大值。如果这个值大于0则表示这个mid是可以达成的。

代码

#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;
const double ep = 0.00001;
int n,L,R;
double a[20010],b[20010],d[80040],sum[20010];
inline void build(int le,int ri,int wh){
      if(le==ri){
          d[wh]=sum[le];
          return;
      }
      int mid=(le+ri)>>1;
      build(le,mid,wh<<1);
      build(mid+1,ri,wh<<1|1);
      d[wh]=max(d[wh<<1],d[wh<<1|1]);
      return;
}
inline double q(int le,int ri,int x,int y,int wh){
      if(le>=x&&ri<=y)return d[wh];
      int mid=(le+ri)>>1;
      double ans=-1e18;
      if(mid>=x)ans=max(ans,q(le,mid,x,y,wh<<1));
      if(mid<y)ans=max(ans,q(mid+1,ri,x,y,wh<<1|1));
      return ans;
}
inline bool ck(double mid){
      for(int i=1;i<=n;i++)
        b[i]=a[i]-mid;
      sum[0]=0;
      for(int i=1;i<=n;i++)
        sum[i]=sum[i-1]+b[i];
      build(1,n,1);
      for(int i=1;i<=n-L+1;i++)
        if(q(1,n,i+L-1,min(n,i+R-1),1)>=sum[i-1])return 1;
      return 0;
}
int main(){
      double le=ep,ri=0;
      scanf("%d%d%d",&n,&L,&R);
      for(int i=1;i<=n;i++){
          scanf("%lf",&a[i]);
          if(a[i]>ri)ri=a[i];
      }
      ri+=ep;
      while(ri-le>ep){
          double mid=(le+ri)/2;
          if(ck(mid))le=mid;
            else ri=mid;
      }
      printf("%0.4lf
",le);
      return 0;
}
原文地址:https://www.cnblogs.com/yzxverygood/p/9699774.html