Mo's Algorithm
AtCoder Biginner Contestに出題されたので、実装してみました。
解説へのリンク(ネタバレ注意)
解説に書かれている以下のリンクに詳細があるので、アルゴリズムについては要約して書きます。
概要
数列についての個の閉区間のクエリにで答える。
やっていることはクエリの平方分割。
条件
- は不変
- クエリ先読み可
- から区間を伸縮しても、値を簡単に計算できる
アルゴリズム
- ブロック番号の昇順、同ブロック内ではの昇順でクエリをソートする
- ソート済みのクエリを左から順に区間を伸縮して値を計算していく
計算量
- 左端はほとんどがブロック内での移動なので、回
- 右端は同ブロック内で高々回の移動なので、回
- 上記を併せて
例
のサイズを9とします。
ブロックサイズ
与えられるクエリ
[5,7]
[1,3]
[3,5]
[4,4]
[7,8]
[2,5]
[0,8]
[7,7]
まず、クエリをソートします。
が属するブロックを第一キーに、を第二キーとして昇順にソートします。
が属するブロックはにより求められます。
ソート後(ブロック毎に区切ってあります。)
[1,3]
[2,5]
[0,8]
[4,4]
[3,5]
[5,7]
[7,7]
[7,8]
ソート後に順にクエリを処理します。
例えば、予め[2,5]の値は計算されていると仮定して[2,5] -> [0,8]を計算するときは、
- 右端を、[2,5] -> [2,6] -> [2,7] -> [2,8]という風に、+1ずつ区間を右に伸長しながら値を更新する
- 左端を、[2,5] -> [1,5] -> [0,5]という風に、-1ずつ区間を左に伸長しながら値を更新する
という感じです。