BZOJ2135 刷题计划(贪心+二分)

  相邻数作差后容易转化成将这些数最多再切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;
}

猜你喜欢

转载自www.cnblogs.com/Gloid/p/9552226.html
今日推荐