Problem 6 二分

Posted shandongs1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Problem 6 二分相关的知识,希望对你有一定的参考价值。

$des$
有 $n$ 个物品,第 $i$ 个物品有两个属性 $k_i, b_i$ ,表示它在时刻 $x$ 的价值为 $k_i
imes x + b_i$ .
当前处于时刻 $0$ ,你可以选择不超过 $m$ 个物品,使得存在某个整数时刻 $t$, $t >= 0$
,你选择的所有物品的总价值大于等于 $S$ .
给出 $S$,求 $t$ 的最小值。

技术分享图片

$sol$
选择任意一个集合,得到的收益和都可以表示为一个一次函数的形式。只关心这些
一次函数的最大值,可以发现这个最大值一定是先降后增的(当 t 非常大时,$k_i imes t > b_i$, 也有可能是单调递增或者单调递减)。
因此只需要 check 一下 0 时刻是否符合条件,如果不符合则进行二分。
注意 check 的时候只需要找出最大的 m 个即可,因此可以 O(n) 地做,具体做法是快排
的过程中只递归一边。直接用 STL 的 nth_element() 即可。
时间复杂度 $O(nlog10^9)$

$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
", Ans);

    return 0;
}

 

以上是关于Problem 6 二分的主要内容,如果未能解决你的问题,请参考以下文章

Problem F Free Weights

C - 4 Values whose Sum is 0(二分)

VSCode自定义代码片段6——CSS选择器

POJ-2452 Sticks Problem 二分+RMQ

[FZU2203] 单纵大法好(二分)

D. Minimax Problem(二分&状压)