[USACO17JAN]Building a Tall Barn建谷仓

题目描述

Farmer John is building a brand new, NNN -story barn, with the help of his KKK cows ( 1≤N≤K≤10121 \leq N \leq K \leq 10^{12}1NK1012 and N≤105N \leq 10^5N105 ). To build it as quickly as possible, he needs your help to figure out how to allocate work among the cows.

Each cow must be assigned to work on exactly one specific floor out of the NNN total floors in the barn, and each floor must have at least one cow assigned to it. The iii th floor requires aia_iai units of total work, and each cow completes one unit of work per hour, so if ccc cows work on floor iii , it will be completed in ai/ca_i / cai/c units of time. For safety reasons, floor iii must be completed before construction can begin on floor i+1i+1i+1 .

Please compute the minimum total time in which the barn can be completed, if the cows are allocated to work on floors in an optimal fashion. Output this number rounded to the nearest integer; it is guaranteed that the solution will be more than 0.1 from the boundary between two integers.

FJ正在他的K头奶牛的帮助下建造一个全新的N层谷仓(1<=N<=K<=10^12,N<=10^5)。为了能够尽快的建造它,他需要你帮助他来找出如何在奶牛间分配工作。

每一头牛必须分配到N层中的某一个特定楼层中,并且每一层楼必须至少有一头牛在其中工作。第i层楼需要a[i]个单位的工作,并且每一头牛完成每一单位的工作需要一个单位时间。所以如果有C头牛在第i层工作,那么第i层需要a[i]/c个单位时间。为了安全起见,在施工开始于在第i+1层楼上前,必须先完成第i层。

如果奶牛被分配以最佳方式在楼层上工作,请计算完成谷仓的最小总时间。输出这个时间四舍五入到整数的结果;题目保证解答离两个整数之间的边界大于0.1。

输入输出格式

输入格式:

The first line of input contains NNN and KKK .

The next NNN lines contain a1…aNa_1 \ldots a_Na1aN , each a positive integer of size at most 101210^{12}1012 .

第一行包括两个数N和K。

接下来N行包括了a[1]...a[n],每行一个不大于10^12的正整数。

输出格式:

Please output the minimum time required to build the barn, rounded to the

nearest integer.

请输出完成谷仓的最小总时间(四舍五入至最接近的整数)。

输入输出样例

输入样例#1: 
2 5
10
4
输出样例#1: 
5

提交地址: Luogu3606

答案抄的真爽, 就是抄了一晚上,WA了n次,才找出来错误, 崩溃ing...

这道题其实一眼就知道二分答案, 想都没想直接敲,16分,what...f?

回过头再仔细读题, 发现这题并不简单;

我们知道a[1],a[2]...a[n],也知道k,我们要求a[1]/b[1]+a[2]/b[2]+...+a[n]/b[n]的最小值,其中Σbi=k;

这仿佛不是一个寻常的二分可以搞定的;

仔细琢磨题解你会发现,我们要让其中每一层楼,不加入一个牛,比加入一个牛更优,什么意思?
就是我们考虑, 如果这个楼层加一个牛比不加一个牛优,那我们肯定会把别的楼层的牛拿过来给他;
那我们该怎么表示这个抽象的东西呢?

我们设:ti = ai / (ci + 1) - ai / ci , ci是第i层楼的牛数量;
这样我们设出来的ti,就表示这个楼层多一只牛的改变量,我们要求的最终答案,肯定是每层楼的ti最接近,否则我们肯定可以找一只牛放里面;

化简一下得到 ti = ai / (ci * (ci + 1)) ,移项得 ci^2 - ci - ai/ti = 0;

发现我们只要枚举ti, 就可以通过简单的小学数学,把ci算出来;

然后开开心心的二分;

最后如果有空闲的牛就直接乘以r,然后减掉就行了,为什么要乘r?
因为我们二分出来了一个ti,这个ti保证是所有楼层的ti(因为他们最接近的时候才是答案),所以直接乘ti,当成他们的贡献!

有几个坑点,首先记得开long long,还得记得二分一个小数一定要设eps...

Code:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cmath>
 5 using namespace std;
 6 #define int long long
 7 #define ll long long
 8 double eps = 1e-9;
 9 ll n, k;
10 double a[100010];
11 
12 double l = 1e-9, r = 12, mid;
13 double ans;
14 ll now;
15 
16 inline bool check(double x) 
17 {
18     now = 0;
19     double res = 0;
20     for (register ll i = 1 ; i <= n ; i ++)
21     {
22         double h = (sqrt(1 + 4 * a[i] / x) - 1) / 2;
23         ll f = ceil(h);
24         now += f;
25         if (now > k) return 0;
26     }
27     return 1;
28 }
29 
30 signed main()
31 {
32     scanf("%lld%lld", &n, &k);
33     for (register ll i = 1 ; i <= n ; i ++) cin >> a[i];
34     
35     
36     while (r - l > eps)
37     {
38         mid = (l + r) / 2;
39         bool fl = check(mid);
40         if (fl) r = mid;
41         else l = mid;
42     }
43     
44     ans = 0.0, now = 0;
45     for (register ll i = 1 ; i <= n ; i ++)
46     {
47         double h = (sqrt(1 + 4 * (a[i] / r)) - 1) / 2;
48         ll f = ceil(h);
49         ans += a[i]/f;
50         now += f;
51     }
52     
53     printf("%.0lf", ans - (k - now) * r);
54     return 0;
55 }


猜你喜欢

转载自www.cnblogs.com/zZh-Brim/p/9135832.html