フワッティーの挑戦日記

深層学習、Atcoderなどの勉強の軌跡を綴っていきます

AtCoder Beginner Contest(ABC) 193 C - Unexpressed の解説

初めに

この間のAtCoder Beginner Contest(ABC)のC問題が解けなかったので、復習を兼ねて簡単な解説を書きます。 使用言語はpythonを想定しています。

C - Unexpressedの問題文

整数Nが与えられます。1以上N以下の整数のうち、2以上の整数 a, bを用いてa^ bと表せないものはいくつあるでしょうか? ただし、 Nは整数で 1 \leq N \leq 10^ {10}である。

解答までの道のり

まずは問題文をよく見る

この問題分を見て私みたいな素人が真っ先に思いつくのがありうる a, bに対して a^ b N以下かどうかをカウントするという方法です。 しかし、競プロでは 2秒で 10^ 8程度しか計算できないため、今回の 1 \leq N \leq 10^ {10}という制約の中ではこの方法は取れません。 よって、もう少しスマートな解法を見つける必要があります。

実験してみる

スマートな解法を見つけるために実験をします。 とりあえず20までの整数を並べて考えてみましょう。

 n  a^ b  n  a^ b
 1  11
 2  12
 3  13
 4  2^ 2  14
 5  15
 6  16  2^ 4, 4^ 2
 7  17
 8  2^ 3  18
 9  3^ 2  19
 10  20

並べてみてわかることは、

  •  a^ bで表せる数は少ない
  • 複数の a^ bで表せる数もある

ということです。 これらをもとに解答を考えてみましょう。

解答の道筋を考える

まず a^ bで表せる数は少ないということから考えてみましょう。  a^ bで表せる数は少ないということからは、 工夫して条件を絞れば全探索ができるかもしれないという事がわかります。

早速条件を絞ってみましょう。

まず aについて、 2 \leq bなので、 a^ 2 \leq a^ b \leq Nが成り立ちます。

よって a \leq \sqrt {N}ということがわかります。

 1 \leq N \leq 10^ {10}なので、 1 \leq \sqrt {N} \leq 10^ {5}であり、計算量的には問題ありません。

次に、 bについて、 a^ b \leq Nかつ 2 \leq aなので、  b \leq \log_a N \leq \log_2 Nが成り立ちます。

 1 \leq N \leq 10^ {10}なので、 0 \leq \log_2 N \leq 10\log_2 10であり、計算量的には問題ありません。

更に、複数の a^ bで表せる数もあるということを考えてみましょう。 今回のケースでは、重複をカウントはしないということは問題文からわかります。 重複をカウントしないといったら・・・setですね。 よって、setに値を入れていくことで a^ bで表せる数をカウントできるとわかります。

コーディング

前項で解答の道筋がわかったので、コーディングしてみましょう。

import math
 
N = int(input())
 
# a**2 <= a**b <= N --> a <= N**0.5
max_a = int(N**0.5) + 1
# a**b <= N --> b <= log_a(N) <= log_2(N)
max_b = int(math.log2(N)) + 1
 
s = set() # 2**4 と 4**2 のような重複を消したい
for a in range(2, max_a):
    for b in range(2, max_b):
        val = a**b
        if val > N:
            break
        s.add(val)
 
print(N-len(s))

これが解答となります。

感想

いざ解答を考えてみると、なんでこんな問題がわからなかったのか・・・となりますね。 実験 → 解答の草案 という流れを着実に考えれば容易に解けた問題だと思います。 同じ轍(てつ)は踏まないようにするぞ!