概要
基礎的な DP と,高速化の工夫を問う問題です.
柔らに考えると案外簡単な問題かもしれません.
問題特有の性質と,計算量の見積もりに注意してください.
問題原案:tnodino
略解
1 1 1 次元の DP を考えます.BFS 等でもよいです.
事前に全ての値について答えを求めることができるので,各クエリには定数時間で答えられます.
以降前計算について,(K K K はとり得る最大の値としてよいです.)
[Easy] は愚直に約数の総和を計算すれば O ( K K ) O(K \sqrt K) O ( K K ) 時間で解けます.
[Hard] に正解するためには,約数の総和の求め方を工夫する必要があります.O ( K log K ) O(K \log K) O ( K log K ) 時間程度を許容します.
解説
Ⅰ . Ⅰ. Ⅰ. 操作が満たす性質
正の整数 k k k の正の約数の総和を S k S_k S k とします.
ある正の整数 k k k に対して,k k k は自身の正の約数として 1 1 1 および k k k 自身を持ちますから,明らかに ∀ k ≥ 2 k < k + 1 ≤ S k \forall_{k\geq2}\;k < k+1 \leq S_k ∀ k ≥ 2 k < k + 1 ≤ S k といえます.
つまり,A , B \text A, \text B A , B いずれの値を選んで操作を行っても,x x x の値は減少しません.
(1 1 1 に関して B \text B B の操作を行ったときに限って S 1 = 1 S_1 = 1 S 1 = 1 より変化しませんが,操作の「最小回数」を知ることが目的なので,x = 1 x = 1 x = 1 のときは常に A \text A A を選ぶこととして問題ありません.)
Ⅱ . Ⅱ. Ⅱ. 動的計画法
(動的計画法(DP)についての詳しい解説は他サイト様へ委ねます.よろしければ TGC000-G も参照ください.)
次のような DP を考えます.
ここで,K K K はとり得る最大の値とすることで,全てのクエリに対しての答えを求めたことになります.
定義
D P [ k ] ≔ ( x \mathrm {DP}[k] \coloneqq ( x DP [ k ] : = ( x を k k k に等しくするために必要な操作回数の最小値) ) )
初期状態
D P [ 1 ] = 0 \mathrm {DP}[1] = 0 DP [ 1 ] = 0
D P [ i ] = ∞ ( 2 ≤ i ≤ K ) \mathrm {DP}[i] = \infty \hspace{0.3em} \scriptsize (2 \leq i \leq K) DP [ i ] = ∞ ( 2 ≤ i ≤ K )
遷移
For k ← each [ 1 , 2 , … , K ) : \text{For}\;k \xleftarrow{\text{each}} [1, 2, \ldots, K): For k each [ 1 , 2 , … , K ) :
D P [ k + 1 ] ← min D P [ k ] + 1 ( A \mathrm {DP}[k + 1] \xleftarrow{\min} \mathrm {DP}[k] + 1 \>(\mathrm{A} DP [ k + 1 ] m i n DP [ k ] + 1 ( A の値を選ぶ場合) ) )
D P [ S k ] ← min D P [ k ] + 1 ( B \mathrm {DP}[S_k] \xleftarrow{\min} \mathrm {DP}[k] + 1 \>\>\>\>\>\,\, (\mathrm{B} DP [ S k ] m i n DP [ k ] + 1 ( B の値を選ぶ場合) ) )
「実装例」の項も参照してください.
Ⅲ . Ⅲ. Ⅲ. S k S_k S k の算出
愚直に求める (TLE)
1 ≤ v ≤ k 1 \leq v \leq k 1 ≤ v ≤ k を満たす v v v を全探索し,v v v が k k k の約数ならば S k S_k S k に v v v を加算することで求められます.
この方法の時間計算量は O ( k ) O(k) O ( k ) ですから,DP の遷移も含めると,O ( K 2 ) O(K^2) O ( K 2 ) 時間となって,[Easy] の制約下でも間に合いません.
少し工夫する ([Easy] に正解)
ある正の整数 v v v について,v v v が k k k の約数ならば,k v \frac{k}{v} v k も同時に k k k の約数となります.
つまり,探索する v v v は 1 ≤ v ≤ k 1\ \leq v \leq \sqrt k 1 ≤ v ≤ k を満たすもののみで十分とわかります.
C++ による実装例を次に示します.
\\
もう少し工夫する ([Hard] に正解)
先述した方法は「1 ≤ k ≤ K 1 \leq k \leq K 1 ≤ k ≤ K を満たすすべての k k k について,1 ≤ v ≤ k 1 \leq v \leq \sqrt k 1 ≤ v ≤ k を満たす v v v を全探索し,v v v が k k k の約数ならば S k S_k S k に v v v と k v \frac{k}{v} v k とを加算する」というものでした.
探索対象となる v v v の中には k k k の約数でないものも含まれるため,無駄がありそうです.
逆に,「必ず何かの約数となる」ような値を列挙できれば,かなり効率的であると考えられます.
少し視点を変えて「1 ≤ k ≤ K 1 \leq k \leq K 1 ≤ k ≤ K を満たす k k k の倍数 m ∈ V k ≔ { i k ∣ k ≤ i k ≤ K } m \in V_k \coloneqq \{\, ik\,|\,k \leq ik \leq K \,\} m ∈ V k : = { ik ∣ k ≤ ik ≤ K } それぞれについて,S m S_m S m に k k k を加算する」という方法を考えてみましょう.
実際に,この方法でもすべての 1 ≤ k ≤ K 1 \leq k \leq K 1 ≤ k ≤ K を満たす k k k について,正の約数の総和を列挙できることが分かります.
「k k k の約数を列挙する」代わりに「k k k が約数となる m ( ≤ K ) m \, (\leq K) m ( ≤ K ) を列挙する」といったイメージです.
直感的には後者の方が無駄がなく効率がよさそうですね.
これをより確実に評価してみましょう.
∣ V k ∣ = ⌊ K − k k ⌋ = ⌊ K k ⌋ − 1 |V_k| = \lfloor \frac{K-k}{k} \rfloor = \lfloor \frac{K}{k} \rfloor - 1 ∣ V k ∣ = ⌊ k K − k ⌋ = ⌊ k K ⌋ − 1 となり,V k V_k V k の要素は O ( ∣ V k ∣ ) O(|V_k|) O ( ∣ V k ∣ ) 時間で列挙できますから,1 ≤ k ≤ K 1 \leq k \leq K 1 ≤ k ≤ K を満たすすべての k k k について V k V_k V k の要素を列挙しようとすると ∑ 1 ≤ k ≤ K ( ⌊ K k ⌋ − 1 ) ⋍ K ∑ 1 ≤ k ≤ K 1 k ⋍ K log K \displaystyle \sum_{1 \leq k \leq K} \Big( \lfloor \frac{K}{k} \rfloor - 1 \Big) \backsimeq K \sum_{1 \leq k \leq K} \frac{1}{k} \backsimeq K \log K 1 ≤ k ≤ K ∑ ( ⌊ k K ⌋ − 1 ) ⋍ K 1 ≤ k ≤ K ∑ k 1 ⋍ K log K 回程度の計算が必要になります.
しかしこれならば [Hard] の制約 K ≤ 2 × 1 0 6 K \leq 2\times 10^6 K ≤ 2 × 1 0 6 のもとでも十分に間に合います.
なお,調和数🔗 が対数程度の速度で発散するという事実を知らなくても,(プログラムや電卓を用いて)以上の(総和を用いた方の)式へ実際に K = 2 × 1 0 6 K = 2 \times 10^6 K = 2 × 1 0 6 を代入して計算してみることでも 確かに間に合いそうだ ということが確かめられるはずです.
Ⅳ . Ⅳ. Ⅳ. 発展
1 ) 1) 1 ) 類題
実装例
S k S_k S k の値は DP の遷移の前に計算してもよいですが,DP を更新しながら同時に計算していくこともできます.
こちらの方が実装が少々楽です.
\\
余談
調和数...
どこか見覚えがありますね
ええ.まさか被るとは思いませんでした.(ABC272-E)
で,でも.この問題の原案が出たのは 8/19 時点なのでわざとじゃないですよ...??(
許してね☆