目次

JOI 2013/2014 春合宿 day1-3 歴史の研究

問題

https://atcoder.jp/contests/joisc2014/tasks/joisc2014_c

TLE解法

まず、数列のとりうる値が大きいので、座標圧縮をしておく。そして、次のような配列をもっておく。

また、区間 $ \lbrack l, r) $ に対する $ cnt_{i}$ の値を $ cnt_{i} (l, r) $ とすると、クエリ $ \lbrack l, r)$ に対する答え $ f (l, r) $ は、

になる。
$ z_{i} \cdot cnt\_{i} ( l, r) $ の値をmultisetで管理すると、最大値の取得、区間の伸縮が $ O(\log N)$ で行えるので、Mo's algorithmを使うことによって、全体で $ O( ( N + Q) \sqrt{N} \log N)$ で問題を解くことができる。
この解法を実装するとTLEしてしまった(定数倍が小さければ通るかもしれない)。

https://atcoder.jp/contests/joisc2014/submissions/7799486

計算量改善

上の解法のネックは、区間を縮める操作に対応するためにすべての値をmultisetで持っておく必要があるところ。区間を広げる操作のみを行えば、解は単調に増加するので最大値以外を持っておく必要がない。

そこで、Moを少し改変してみることにする。左端を $ \sqrt{N}$ ずつのブロックに分け、ブロックが同じところでは右端でソートするというところまでは同じ。

ただし、クエリの処理はブロックごとに独立に行う(リセットする)ことにする。こうすると、$r$ は単調増加になるので、右端は考慮しなくてよい。

左端はどうするかというと、初期位置を次のブロックとの境界にしておく。区間の答え $ x$ (初期値0)を持っておき、クエリごとに次の順で操作を行う。

  1. 右端を右に動かしながら $ cnt, x$ を更新する(このときの $ x$ を保存しておく)
  2. 左端を左に動かしながら $ cnt, x$ を更新する(このときの $ x$ がクエリの答え)
  3. 左端を右に動かし初期位置に戻す( $ x$ を1.の時点のものに戻す)

計算量を解析してみる。

よって、この操作の計算量は$O((N+Q) \sqrt{N})$になり、余裕を持って通すことが出来る。

ACコード

https://atcoder.jp/contests/joisc2014/submissions/7800479

最後に

このアルゴリズムは Rollback平方分割 と名がつけられているらしいです。
追加が軽くて、削除が重いときはこれを使うとよさそう

https://snuke.hatenablog.com/entry/2016/07/01/000000