P1570 KC 喝咖啡 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
让求 ∑ v i ∑ c i \frac { \sum{ v_i}} { \sum{c_i}} ∑ci∑vi的最大值,假设值为 a n s ans ans,则
a n s = ∑ v i ∑ c i ans = \frac {\sum{v_i}} {\sum{c_i}} ans=∑ci∑vi
进行移项做恒等变换,得:
a n s × ∑ c i − ∑ v i = 0 ans \times \sum{c_i} - \sum{v_i} = 0 ans×∑ci−∑vi=0
可以发现,最优解一定是尽可能让这个式子为0。
此时,就找到二段性。
令 v a l = a n s × ∑ c i − ∑ v i val = ans \times \sum{c_i} - \sum{v_i} val=ans×∑ci−∑vi。
- 若 v a l ≤ 0 val \le 0 val≤0: a n s ans ans更大可能更优
- 若 v a l > 0 val \gt 0 val>0: a n s ans ans更小可能更优
因此套用最小值最大的二分模板即可。
如何快速的找到 m m m个调料进行融合:根据二分的 a n s ans ans值对第调料按 a n s × c − v ans \times c - v ans×c−v进行排序即可。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 21;
struct no {int v,c;double w;bool operator<(const no & rhs) {return w < rhs.w;}
}a[N];
int main()
{int n,m; cin>>n>>m;for(int i = 1; i <= n; ++i) cin>>a[i].v;for(int i = 1; i <= n; ++i) cin>>a[i].c;double l = 0, r = 1e9;auto check = [&](double mid) -> bool {for(int i = 1; i <= n; ++i) a[i].w = a[i].c * 1.0 * mid - a[i].v;sort(a + 1, a + n + 1);double sum = 0;for(int i = 1; i <= m; ++i) sum += a[i].w;return sum <= 0;};while(l < r - 1e-6) {double mid = (l + r) / 2;if(check(mid)) l = mid;else r = mid;}printf("%.3lf",l);
}