[USACO14MAR]破坏Sabotage

还是二分答案,发现我的$check$函数不太一样,来水一发题解

列一下式子

$$frac{sum-sum[l,r]}{n-(r-l+1)}<=ans$$

乘过去

$$sum-sum[l,r]<=ans*(n-r+l-1)$$



$$sum_{i=1}^{l-1}+sum_{i=r+1}^{n}<=ans*(n-r+l-1)$$

$$sum{(a_i-ans)}<=0$$

所以我们在$check$函数中,可以处理出$a_i-ans$数组

然后求个前缀和,后缀和,前缀最小值,后缀最小值,对于每个位置考虑这个位置的前缀最小和这个位置之后(因为不能一个不删)的后缀最小是否非负即可

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=1e5+10;
const double eps=1e-6;
int n,m[maxn];
double ans,sum1[maxn],sum2[maxn],mn1[maxn],mn2[maxn],a[maxn];
bool check(double x)
{
    for(int i=0;i<=n+1;i++)
        mn1[i]=mn2[i]=1e9;
    for(int i=1;i<=n;i++)
        a[i]=m[i]-x;
    for(int i=1;i<=n;i++)
        sum1[i]=sum1[i-1]+a[i],mn1[i]=min(sum1[i],mn1[i-1]);
    for(int i=n;i;i--)
        sum2[i]=sum2[i+1]+a[i],mn2[i]=min(sum2[i],mn2[i+1]);
    for(int i=1;i<n-1;i++)
        if(mn1[i]+mn2[i+2]<=0)
            return 1;
    return 0;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&m[i]);
    double l=1,r=10000;
    while(r-l>eps)
    {
        double mid=(l+r)/2;
        if(check(mid))
            r=mid-eps,ans=mid;
        else
            l=mid+eps;
    }
    printf("%.3lf
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/ivanovcraft/p/9783843.html