相邻数作差后容易转化成将这些数最多再切m刀能获得的最小偏差值。大胆猜想化一波式子可以发现将一个数平均分是最优的。并且划分次数越多能获得的偏差值增量越小。那么就可以贪心了:将所有差扔进堆里,每次取出增量最大的。
因为m非常大这样还是不行。考虑二分我们所获得的最小偏差值增量。可以解解方程计算出一个数在该情况下至多能被切多少次。加起来和m比一下就可以继续二分了。注意与wqs二分类似最后可能无法恰好二分到m,需要微调一下。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
#define N 50010
const double eps=1E-;
int n,m;
double l,a[N],ans;
double calc(double x,int cnt)
{
return (x/cnt-l)*(x/cnt-l)*cnt;
}
int getcnt(double a,double b)
{
return (int)((sqrt(+*a*a/(b+l*l))-)/+);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj2135.in","r",stdin);
freopen("bzoj2135.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read();cin>>l;
for (int i=;i<=n;i++) cin>>a[i];
for (int i=;i<n;i++) a[i]=a[i+]-a[i];
n--;
double l=eps,r=,delta=,delta2;
while (r-l>eps)
{
double mid=(l+r)/;
int cnt=;double tot=;
for (int i=;i<=n;i++)
cnt+=getcnt(a[i],mid)-,tot+=calc(a[i],getcnt(a[i],mid));
if (cnt<=m) ans=tot,delta2=m-cnt,r=mid-eps;
else delta=mid,l=mid+eps;
}
printf("%.3lf",ans-delta*delta2);
return ;
}