walking dictionary

2 secs 1024 MB
naskya's icon naskya

vocabulary の解説 と同様に N×(N1)×(N2)××(Ni+1)N \times (N - 1) \times (N - 2) \times \cdots \times (N - i + 1)P(N,i)P(N, i) と表記します。

解説

入力の受け取りには多倍長整数を使用するか、(NN101810^{18} で割った余りしか出力に影響しないことから) NN を文字列として受け取り、その下 1818 桁のみを 64 bit 整数に変換するなどの処理を行えばよいです。

vocabulary と同様、この問題も i=1NP(N,i)\displaystyle\sum_{i = 1}^N P(N, i)101810^{18} で割った余りを求める問題となります。

ここで、任意の整数 m,nm, n に対して mm101810^{18} で割った余りと (m+n1018)(m + n \cdot 10^{18})101810^{18} で割った余りは等しいことから、P(N,i)P(N, i)101810^{18} の倍数となる場合にはその項の計算を行わなくても出力に影響しないことが分かります。そこで、P(N,i)P(N, i)101810^{18} の倍数となる ii の条件を見つけてその部分の計算を省略することを考えます。

P(N,i)P(N, i) は連続する ii 個の整数の積であるため、1018i10^{18} \leq i なる ii について P(N,i)P(N, i)101810^{18} の倍数となることは直ちに分かります(連続する 101810^{18} 個の整数の中には 101810^{18} の倍数が含まれるため)。したがって、i=1min(N,10181)P(N,i)\displaystyle\sum_{i = 1}^{\min(N, 10^{18} - 1)} P(N, i) を計算するだけで答えが得られます。

もう少し考えると、1018=218×51810^{18} = 2^{18} \times 5^{18} より連続する 101810^{18} 個の整数でなくても 22 の倍数と 55 の倍数がそれぞれ 1818 個以上含まれるような区間の整数の積は 101810^{18} の倍数となることが分かります。例えば連続する 9090 個の整数に 22 の倍数と 55 の倍数はそれぞれ 1818 個以上含まれるので、90i90 \leq i なる ii について P(N,i)P(N, i)101810^{18} の倍数となります。(更に言うと、連続する 2525 個の整数の中には 25=5225 = 5^2 の倍数が 11 つ含まれ、その分 55 の素因数を「稼げる」ため ii の条件は 75i75 \leq i で十分です。)

以上より i=1min(N,74)P(N,i)\displaystyle\sum_{i = 1}^{\min(N, 74)} P(N, i)101810^{18} で割った余りを計算すればよく、この計算は十分高速に行えます。

解答例 (C++)


このようにすると、法とする値 (この問題では 101810^{18}) が i=1npiqi\displaystyle\prod_{i = 1}^{n} p_i^{q_i} と素因数分解されるとき(整数のコピーや四則演算等の操作の時間計算量が O(1)O(1) であることを仮定すると) 時間計算量 O(max1inpiqi)O \left(\displaystyle\max_{1 \leq i \leq n} p_i \cdot q_i \right) でこの問題を解くことができます。

例えば「10910^9 で割った余りを求めよ」という問題も同様に解くことができますが、「109+710^9 + 7 で割った余りを求めよ」という問題をこの方法で解いて実行時間制限に間に合わせることは厳しいです(109+710^9 + 7 は大きな素数であるため)。