[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\n",ans); return 0; }