拖了许久的坑。。
sum[i]=∑t=1ia[t]
sum1[i]=∑t=1i1a[t]
sum2[i]=∑t=1isum[t]a[t]
f[i][k]前i个数,分成k部分的最小得分。
f[i][k]=min{f[j][k-1]+sum2[i]-sum2[j]-sum[j]*(sum1[i]-sum1[j]|k-1<=j< i)标准的2d/1d方程,进行斜率优化,设k1< k2,k1优于k2,则见代码。。还是维护下凸曲线。。然后这题精度有毒。也不知道能不能过。。。
#include <cstdio>
#include <cstring>
#define N 200010
#define ll long long
int n,k,a[N],p=0,q[N],h=0,t=1;
ll sum[N];
double sum1[N],sum2[N],f[2][N];
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline double slope(int k1,int k2){
return (f[p][k2]-f[p][k1]+sum2[k1]-sum2[k2]+sum[k2]*sum1[k2]-sum[k1]*sum1[k1])/(sum[k2]-sum[k1]);
}
int main(){
freopen("math.in","r",stdin);
freopen("math.out","w",stdout);
n=read();k=read();
for(int i=1;i<=n;++i) a[i]=read(),sum[i]=sum[i-1]+a[i];
for(int i=1;i<=n;++i){
sum1[i]=sum1[i-1]+1.0/a[i];
sum2[i]=sum2[i-1]+sum[i]*1.0/a[i];
}
for(int i=1;i<=n;++i) f[p][i]=sum2[i];
for(int x=2;x<=k;++x){
h=0,t=1;q[++h]=x-1;
for(int i=x;i<=n;++i){
while(h<t&&slope(q[h],q[h+1])<sum1[i]) ++h;
f[p^1][i]=f[p][q[h]]+sum2[i]-sum2[q[h]]-sum[q[h]]*(sum1[i]-sum1[q[h]]);
while(h<t&&slope(q[t],i)<slope(q[t-1],q[t])) --t;
q[++t]=i;
}p^=1;
}
printf("%.15lf\n",f[p][n]);
return 0;
}