P3606 [USACO17JAN]Building a Tall Barn建谷仓

来源:互联网 发布:java方法之间调用 编辑:程序博客网 时间:2024/05/16 15:43

usaco 铂金组t2

题目大意:已知的a1,a2,……an,在b1+b2+……+bn=k的条件下,求f=a1/b1+a2/b2……+an/bn的最小值。

题目分析:如果需要f尽量的小,则我们希望分母尽可能大,,而总数有要求限制,那么问题来了,给谁分配多点,给谁分配少点呢?

例如:

3 10
20
5
10的数据情况下。,给每个ai分配y个或(y+1)个人产生不同的差值。令ti=ai/y-ai(y-1),如果ti>tj,那么,我们从bj调动一 个人去bi效果会更优。什么时候达到最优呢?那就是无人可调的情况下,也就是所有的ti尽可能接近的情况!


根据题意,ti越大,需要的人越少,ti越小,需要的人越多,而且题目里面的数据范围是1e12,这么大的数据范围,而且单调,当然用二分答案来做啦。

根据ti=ai/y-ai(y-1),可以得到y*y+y-ai/ti=0,二分ti,计算y的和,超过k那么范围扩大,否则缩小。

代码:洛谷题解

#include<iostream>#include<cstdio>#include<algorithm>#include<iomanip>#include<cmath>#define INF 100000000000000LL#define eps 1e-13using namespace std;#define ll long longint n;ll K;ll ans=INF;double a[100005]; ll cal(double x){return (ll)((sqrt(1+4*x)-1)/2);} bool check(double x){    ll left=K;    for(int i=1;i<=n;i++)    {        if(a[i]<=x) continue;        ll xx=cal(a[i]/x);        left-=xx;    }    //cout<<x<<" "<<left<<endl;    if(left<0) return false;    return true;} int main(){        cin>>n>>K;    K=K-n;    for(int i=1;i<=n;i++) cin>>a[i];    double l=eps,r=1e12,mid;    for(int i=1;i<=200;i++)      {        mid=(l+r)/2;        if(check(mid)) r=mid;        else l=mid;        //cout<<l<<" "<<r<<endl;    }    int i;    for(i=1,l=0;i<=n;++i){        double tmp=(double)(cal(a[i]/r)+1);        l+=a[i]/tmp;        //cout<<i<<" cal "<<a[i]<<"/"<<r<<"= "<<"tmp"<<"  day"<<a[i]/tmp<<endl;    }    cout<<(ll)(l+0.5);    return 0;}


原创粉丝点击