Official

D - Yet Another Recursive Function Editorial by nok0


この問題は メモ化再帰 と呼ばれるテクニックの練習問題です。

まず、愚直に再帰をする解法の計算量を考えてみましょう。

\(f(0)\) が呼ばれる回数を考えると、これは \(2\) で割ることと \(3\) で割ることを繰り返して \(0\) に到達する方法の総数と一致します。小さく見積もっても、\(\binom{2\log_3 N}{\log_3 N}\) 程度はあり、\(N=10^{18}\) のとき明らかに間に合いません。

次に、メモ化再帰を行う場合の計算量を考えてみましょう。

\(f\) の引数として取りうる値の種類数を考えます。引数として取りうる値は、非負整数 \(x,y\) を用いて、\(\displaystyle\Big \lfloor \frac{N}{2^x3^y}\Big\rfloor\) という形で表されるものに限ることがわかります。

雑に見積もっても、\(x+y\)\(\log_2{N}\) 以下の値しか取らないので、引数の種類数は \(\mathrm{O}(\log^ 2N)\) です。以上より、メモ化再帰を用いることでこの問題を \(\mathrm{O}(\log ^2N)\) で解くことができました。

実装例(python):

from functools import lru_cache


@lru_cache
def f(n):
    if n == 0:
        return 1
    return f(n // 2) + f(n // 3)


print(f(int(input())))

posted:
last update: