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-13
using namespace std;
#define ll long long
int 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;
}