二分探索 atcoder

ABC146 C - Buy an Integer

ABC146 C - Buy an Integer
Feb. 2, 2020, 1:52 p.m.

目次

問題

https://atcoder.jp/contests/abc146/tasks/abc146_c

入力

A B X
  • A, B - $1 \leq A, B \leq 10^9$を満たす整数
  • X - $1 \leq X \leq 10^18$を満たす整数

出力

$A \times N + B \times d(N) \leq X$を満たすような最大の$N$を答える問題です.
ただし$d(N)$は$N$の桁数です.

N

入出力例

10 7 100
9

解説

条件を満たす場合にtrueを返す関数をcanBuyとします.
$N$が1以上$10^9$以下なので、$N = 1$からNを1つずつ大きくしていってcanBuyを実行します.
そして初めてfalseを返したらそれより1つ小さい値が答えになります.

これは線形探索なので計算ステップ数的には$10^9$となり間に合いません.
$10^7$程度に抑える必要があります.

そこでNの性質に着目すると$N = 1, 2, 3,..., 10^9$と単調に増加しています.
これはソート済みの配列とみなすことが出来ます.
ソート済みの配列には二分探索が適用できます.

二分探索の場合は計算量$O(\log{10^9})$なので余裕で間に合います.

二分探索の場合には範囲を漏れなくチェックすることに気をつけることが重要です.
探索範囲の左をleft、右をright、その中央をn、前回のnをprevとします.
更新ロジックと終了条件を以下のようにすると簡単に漏れなくチェックできます.

更新

  • 1) nが買える時、left = n + 1
  • 2) nが買えない時、right = n - 1

終了条件は以下どちらかを満たす場合

  • 1) leftrightが逆転した場合
  • 2) nprevが同じ場合

範囲外の$N$についても事前にチェックしておきましょう.
Nの最大値$10^9$が買える場合にはすべての$N$が買えます.
1すら買えない場合には何も買えません.

あとは通常の二分探索です.
以下のコードを参照してください.

計算量

整数$N$の最大値は$10^9$なので、これを二分探索すると、

$$
\log{10^9} = 9 \log{10} \approx 29.89
$$

解答

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#define MAX_N 1000000000
#define MIN_N 1
Int A, B, X;

void input() {
  cin >> A >> B >> X;
}

Int d(Int n) {
  return to_string(n).length();
}

bool canBuy(Int n) {
  return A * n + B * d(n) <= X;
}

void solve() {
  // なんでも買える
  if (canBuy(MAX_N)) {
    cout << MAX_N << endl;
    return;
  }

  // 何も買えない
  Int max_n = MIN_N;
  if (!canBuy(max_n)) {
    cout << 0 << endl;
    return;
  }

  // 最高値を二分探索
  Int left = MIN_N+1, right = MAX_N-1;
  Int prev = -1, n;
  while (left <= right) {
    n = (left+right)/2;
    if (n == prev) break;
    prev = n;
    if (canBuy(n)) {
      max_n = n;
      left = n + 1;
    } else {
      right = n - 1;
    }

  }
  cout << max_n << endl;
}

int main(void) {
  input();
  solve();
  return 0;
}