相邻数作差后容易转化成将这些数最多再切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=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 50010 const double eps=1E-11; 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(1+4*a*a/(b+l*l))-1)/2+1); } 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=1;i<=n;i++) cin>>a[i]; for (int i=1;i<n;i++) a[i]=a[i+1]-a[i]; n--; double l=eps,r=10000,delta=0,delta2; while (r-l>eps) { double mid=(l+r)/2; int cnt=0;double tot=0; for (int i=1;i<=n;i++) cnt+=getcnt(a[i],mid)-1,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 0; }