Problem 6 二分
des
有 n 个物品,第 i 个物品有两个属性 ki,bi ,表示它在时刻 x 的价值为 ki×x+bi .
当前处于时刻 0 ,你可以选择不超过 m 个物品,使得存在某个整数时刻 t, t>=0
,你选择的所有物品的总价值大于等于 S .
给出 S,求 t 的最小值。
sol
选择任意一个集合,得到的收益和都可以表示为一个一次函数的形式。只关心这些
一次函数的最大值,可以发现这个最大值一定是先降后增的(当 t 非常大时,ki×t>bi, 也有可能是单调递增或者单调递减)。
因此只需要 check 一下 0 时刻是否符合条件,如果不符合则进行二分。
注意 check 的时候只需要找出最大的 m 个即可,因此可以 O(n) 地做,具体做法是快排
的过程中只递归一边。直接用 STL 的 nth_element() 即可。
时间复杂度 O(nlog109)
code
#include <bits/stdc++.h> #define Rep(i, j, k) for (int i = j; i <= k; i++) using namespace std; int Read() { char c = getchar(); int x = 0; int sig = 1; while (c < '0' || c > '9') { if (c == '-') sig = -1; c = getchar(); } while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * sig; } const int N = 1e6 + 10; typedef long long LL; int n, m; LL S; int k[N], b[N]; LL val[N]; bool check(int x) { Rep(i, 1, n) val[i] = 1ll * k[i] * x + b[i]; nth_element(val + 1, val + m, val + n + 1, greater<LL>()); LL sum = 0; Rep(i, 1, m) if (val[i] > 0 && (sum += val[i]) >= S) return true; return false; } int main() { n = Read(), m = Read(); scanf("%lld", &S); Rep(i, 1, n) k[i] = Read(), b[i] = Read(); if (check(0)) {puts("0"); return 0;} int L = 1, R = 1e9, Ans; while (L <= R) { int mid = (L + R) / 2; if (check(mid)) Ans = mid, R = mid; else L = mid + 1; } printf("%d\n", Ans); return 0; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步