F - (Divisors Counter)^{-1}

2 secs 1024 MB
uni_kakurenbo's icon uni_kakurenbo

概要

約数の集合の包括関係を考えると,貪欲に答えを決定していくことができます.

問題原案:uni_kakurenbo

解説

操作によって f(n)(nZ+)f(n) \scriptsize \;(n \in \Z^+) の値がそれぞれどのように変化するかを考えてみましょう.

ff の定義から,非負整数 xx の追加によって f(n)(nZ+)f(n) \scriptsize \;(n \in \Z^+) の値が増加するとき,「xxnn の(正の)約数」です.

なお,いかなる操作によっても f(n)(nZ+)f(n) \scriptsize \;(n \in \Z^+) の値が減少することはありませんし,ここでは NN 以下の正整数 nn に関してのみ f(n)f(n) の値に関心があるので,列 XXNN 以上の数を追加することは考えなくてよいことが分かります.NN 以上の整数を追加したとき f(n)f(n) の値が増加するような NN 以下の正整数 nn は存在しないからです.

さらに,「xxnn の(正の)約数」であることは「nnxx の(正の)倍数」であることに等しいですから,この問題を次のように読み替えることもできます:

  • 長さ NN の列 X=(X1,X2,,XN)X' = (X'_1, X'_2, \ldots, X'_N) があり,はじめ X1=X2==XN=0X'_1 = X'_2 = \cdots = X'_N = 0 である.
  • XX' に対して以下の操作を何度でも行うことができる:
    • 1xN1 \leq x \leq N なる整数 xx を自由に選び,i=kx(kZ+kxN)i = kx \scriptsize \; (k \in \Z^+ \land kx \leq N) と表されるようなすべての相異なる ii について XiX'_i11 を加算する.
  • A=XA = X' とするための操作回数の最小値を求めよ.

この言い換えは必ずしも必要ではないですが,こうすることで少しばかり見通しがよくなるかもしれません.以下では,読み替える前の元の問題を主に扱います.

ここで,操作はどんな順番で行ってもよいので,XX に追加すべき正整数 xx の個数を TxT_x として,この問題の答えは 1nNTn\displaystyle \sum_{1 \leq n \leq N} T_n 回と表されます.

さて,11 の(正の)約数は 11 のみですから,f(1)f(1) の値は XX に含まれる 11 の個数に一致する,すなわち T1=A1T_1 = A_1 であることが分かります.

同様に,

  • 22 の(正の)約数は 1,21, 2 のみだから,T1+T2=A2T_1 + T_2 = A_2
  • 33 の(正の)約数は 1,31, 3 のみだから,T1+T3=A3T_1 + T_3 = A_3
  • 44 の(正の)約数は 1,2,41, 2, 4 のみだから,T1+T2+T4=A4T_1 + T_2 + T_4 = A_4
  • \vdots
  • mnmZ+Tm=An\displaystyle \sum_{\substack{m|n \\m \,\in\, \Z^+}} T_m = A_n

を得ます.

さて,T1=A1T_1 = A_1 であることが分かっていますから,T2=A2T1T_2 = A_2 - T_1 が分かります.

同様に,

  • T3=A3T1T_3 = A_3 - T_1
  • T4=A3(T1+T2)T_4 = A_3 - (T_1 + T_2)
  • \vdots
  • Tn=Anmnm<NmZ+\displaystyle T_n = A_n - \sum_{\substack{m|n \\ m < N \\ m \,\in\, \Z^+}}

と,Tn(1nN)T_n \scriptsize \; (1 \leq n \leq N) の値をすべて確定させることができます.
任意の正整数 pp について,pppp 以外の正の約数はすべて pp 未満であることから従います.

このようにして求めた T1,T2,,TNT_1, T_2, \ldots, T_N の値がすべて非負の整数であるかを調べることで,条件を満たせるかどうかを Θ(NN)\Theta(N \sqrt N) 時間などで判定できますが,N106N \leq 10^6 のもとで実行時間制限に間に合わせることは少々厳しいです.

ここで先ほどの読み替えが役に立つかもしれません.約数よりも倍数の方がプログラムによる列挙が (実装的にも計算量的にも) 簡単なので,より効率よく解くことができるでしょう.
詳しくは実装例も参照してください.

倍数の列挙によって TxT_x の値を求めていくと,実装にも依りますが大抵の場合,計算量(のオーダー)は調和級数の発散速度(のオーダー) (O(logN))(O(\log N))Θ(N)\Theta(N) 倍と同程度になります.

解説:uni_kakurenbo

実装例

C++
Python