[USACO14MAR] Sabotage 二分答案 分数规划
最终答案的式子:
[frac{sum-sum[l,r]}{n-len[l,r]}le ans
]
转换一下:
[sum[1,l-1]+sum[r+1,n]le ans*(len[l,l-1]+len[r+1,n])\
sum (a[j]-ans)le 0 \
(jin[1,l-1],[r+1,n])
]
这里我们先都减去(ans),然后求一个前缀和、后缀和、在(i)点最小的前缀和,在(i)点最小的后缀和,(O(n))枚举断点(也不算断点)如果存在则return 1
#include <cstdio>
#include <algorithm>
#define MAXN 100010
using namespace std;
int n;
int m[MAXN];
double psum[MAXN],lsum[MAXN],psum_min[MAXN],lsum_min[MAXN],a[MAXN];
const double eps=1e-6;
inline bool check(double x){
for(int i=0;i<=n+1;++i) psum_min[i]=lsum_min[i]=1e9;
for(int i=1;i<=n;++i) a[i]=(double)m[i]-x;
for(int i=1;i<=n;++i) psum[i]=psum[i-1]+a[i], psum_min[i]=min(psum_min[i-1], psum[i]);
for(int i=n;i;--i) lsum[i]=lsum[i+1]+a[i], lsum_min[i]=min(lsum_min[i+1], lsum[i]);
for(int i=1;i<n-1;++i)
if(psum_min[i]+lsum_min[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,ans=-1;
while(r-l>eps){
double mid=(l+r)/2;
if(check(mid)) r=mid,ans=mid;
else l=mid;
}
printf("%.3lf", ans);
}