ARC128 F問題:Frog Jump

問題. Frog Jump

 N 個の蓮が一列に浮かんでいる. i \,(0 \le i < N) 番目の蓮の座標は  i でスコアを  s_i とする.正の整数  A, B を決めて次のゲームを 0 番目の蓮から行う.現在の座標を  x として, x + A の蓮に移動する.このとき現在のスコアに  s_{x + A} 加算され, x にある蓮は消滅する.次に, x + A から  x + A - B に移動して現在のスコアに  s_{x + A - B} が加算され, x + A にある蓮が消滅する.このことを繰返し行いゴールである  N - 1 番目の蓮を目指す.途中で蓮の無い場所に移動したらゲームは終了でスコアが  10^{100} が減少される.スコアが最大となる  A, B を求めて,そのときのスコアの答えよ.

制約 3 \le N \le 10^5,  -10^9 \le s_i \le 10^9,  s_0 = s_{N - 1} = 0

解法

snukeさんの 解説 を参考にした.


 A N - 1 に設定することによってスコア 0 でゲームを終了することができるので,スコアを最大とするような移動方法では蓮の無い場所には移動しないことが分かる.
 A, B を固定したときの移動する蓮の座標の列は,
  0, A, A - B, 2 A - B, 2 A - 2 B, 3 A - 2 B, 3 A - 3 B, \ldots, N - 1
となる.偶数番目から奇数番目への座標は右に  A 移動して,奇数番目から偶数番目への座標は左に  B 移動しているので,最後のゴールに移動するときは右に  A 移動する必要があるので, N - 1 は奇数番目になる.ここで, C = A - B と置き上の列を整理すると,
  0, A, C, A + C, 2 C, A + 2 C, 3 C, \ldots, A + (k - 1) C, k C, A + k C \,(= N - 1)
となる. k は適当な非負整数とする.列の偶数番目を取り出すと左から  0, C, 2 C, \ldots, k C となり,奇数番目を取り出すと右から  N - 1, N - 1 - C, N - 1 - 2 C, \ldots, N - 1 - k C となるので, C k に対して全探索を行う. k が1増えるごとに訪れる蓮の数が 2 増えるので高々  O(\log N) しかならない(← 大嘘). k C < N なので, \sum_{c = 1}^{N - 1} \frac{N}{c} \le N \sum_{c = 1}^{N} \frac{1}{c} \le N \log N なので  O(N \log N) 時間である.

計算時間 O(N \log N)

最後の解析で大嘘を言ってしまった.戒めのために残す.
最後らへん適当になってしまった.左と右から伸ばすときに重複した場所を訪れてないかの判定で std::set を使うと  O(N \log^2 N) 時間となる.直接アドレス法で実装すると初期化が必要となり一見  O(N^2) 時間必要かと思うけど,訪れたか訪れていないかを  C の値で管理すると初期化が必要なくなり  O(N \log N) 時間となる. C の値が long long に収まらない場合(ほとんど無いけど・・・)は,ここを参照 すると定数時間で初期化できる.