Best cow fences二分判定前缀和
注意边界
题目描述:
判定是否存在一个长度不小于l的子段,平均数不小于二分的值
问题分析:
-
首先,该问题是一个单调的问题,即每次二分最终答案的规模都会缩小
-
因为平均数不是整数,所以使用实数域的二分法
-
使用二分法之后,将问题转化为是否存在子串,使它的平均数等于x。
如何求解问题3?因为子串是连续的,所以可以联想到前缀和。只需将数组的所有元素都减去平均值,构造差分数组(这是问题成立的条件变成是否存在长度不小于i的子段,它的和等于0),然后使用前缀和,只要两个间距大于l的数前者小于后者,问题就成立。 -
有两点需要注意:当大于0时,说明平均值不够大,需要取右区间,就是l=mid+1
数组下标从1开始,前缀和时不易越界
#include
#include
#include
#define maxx 400005
using namespace std;
double a[100005];
double b[100005];
double c[100005];
int main()
{int n; int f; cin>>n>>f; for(int i=1;i<=n;i++) scanf("%lf",a+i); double eps=1e-5; double l=-1e6,r=1e6; while(r-l>eps) //二分平均值 { double mid=(l+r)/2; for(int i=1;i<=n;i++) b[i]=a[i]-mid; for(int i=1;i<=n;i++) c[i]=c[i-1]+b[i];//求前缀和, double ans=-1e10; double minn=1e10; for(int i=f;i<=n;i++)//这里要注意范围,如果是i从0开始的话, { minn=min(minn, c[i-f]);//不断维护左端的最小值 ans=max(ans, c[i]-minn);//相减得到区间和,不断往正值靠近 } if(ans>=0) //实数域上的二分 l=mid; else r=mid; } cout<<(int)(r*1000);
return 0;}