Mo's algorithmを用いた方法を説明します.
問題はこちら
問題概要
長さの数列に関して「番目から番目までに含まれる数の種類を求めよ」という個のクエリに答えよ.解説
Range Set Queryという名前とか数列の区間に対するクエリとかからSegmentTreeを使いそうだが,区間のマージ操作に時間がかかってしまい無理.
以下の条件を満たすときMo's algorithmというものが使える.
- 更新クエリがない
- の答え(状態)からの答え(状態)が高速に計算できる.
- オフラインで処理できる.
※「オフラインで処理できる」とはの答えを求めるクエリに答える順番は関係ない、という感じです.答えを求めるクエリの間に更新クエリがあるとこれができなくなります.(2022/01/08 追記)
今回の問題はこれを満たしているのでMo's algorithmが使える.
Mo's algorithmの考え方について説明する.
のクエリがある場合にに点をプロットしたような二次元平面上を考える.
入力例での例
このように考えることにより,からへの変化が二次元平面上で上下左右に移動することとみなせる.
この移動を繰り返し,全点をできるだけ少ない移動で訪れる巡回セールスマン問題のようなものになる.
ここで, (横軸)を分割し(二次元平面を縦に切る),分割されたグループごとに順に訪れることにすると, (縦軸)方向の移動回数はつのグループにつき,グループ内で方向の移動回数は点ごとに (1つのグループの方向の長さが)であるから,上下左右に移動する操作がでできるとすると全体の計算量はとなる.これを最小にするようなはなので分割することによりで解くことができる.
今回の問題はなのでで解くことができる.これでもC++などの高速な言語なら間に合う.移動の仕方として,すべてのグループでの昇順に訪れるのではなくて,昇順に訪れるグループと降順に訪れるグループを交互に(蛇行するように)すると速くなる.
入力例での例(緑がグループの境界,初期状態は)
また,ヒルベルト曲線を用いると定数倍速くなるらしい(なんで?)
https://codeforces.com/blog/entry/61203
マンハッタンTSPの2近似アルゴリズムもやってみたが,所詮2近似なので遅かった.
他のMo's Algorithmを使う問題は以下にまとめてます。
kanpurin.hatenablog.com
提出プログラム
https://atcoder.jp/contests/abc174/submissions/21564578https://atcoder.jp/contests/abc174/submissions/22161351 ヒルベルト曲線
https://atcoder.jp/contests/abc174/submissions/26267842 三角形バージョン
巡る順番を決めるフェーズはそんなに重くないので上の三つ全部調べて一番更新回数が少ないものに決めるのもいいかも