[USACO14MAR]破坏Sabotage

时间:2023-03-09 08:38:16
[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+;
const double eps=1e-;
int n,m[maxn];
double ans,sum1[maxn],sum2[maxn],mn1[maxn],mn2[maxn],a[maxn];
bool check(double x)
{
for(int i=;i<=n+;i++)
mn1[i]=mn2[i]=1e9;
for(int i=;i<=n;i++)
a[i]=m[i]-x;
for(int i=;i<=n;i++)
sum1[i]=sum1[i-]+a[i],mn1[i]=min(sum1[i],mn1[i-]);
for(int i=n;i;i--)
sum2[i]=sum2[i+]+a[i],mn2[i]=min(sum2[i],mn2[i+]);
for(int i=;i<n-;i++)
if(mn1[i]+mn2[i+]<=)
return ;
return ;
}
int main()
{
scanf("%d",&n);
for(int i=;i<=n;i++)
scanf("%d",&m[i]);
double l=,r=;
while(r-l>eps)
{
double mid=(l+r)/;
if(check(mid))
r=mid-eps,ans=mid;
else
l=mid+eps;
}
printf("%.3lf\n",ans);
return ;
}