2019年6月26日水曜日

ARC 098 E - Range Minimum Queries

ARC 098 E - Range Minimum Queries

数列A1,A2,...A_1, A_2,...を昇順に並べ替えたものをA1A2...A'_1 \le A'_2 \le ...とする。最大値-最小値をすべて試すなら、だいたい次のようになるはずだ。

  1. 数列(Ai)(A_i)の小さいほうからQQ個取り除いて最大値-最小値を求める。
  2. A1A'_1を残したままにしつつ(Ai)(A_i)からできるだけ小さい数を取り除いてゆき、QQ個取り除けたら最大値-最小値を求める。
  3. A1,A2A'_1, A'_2を残したままにしつつ(Ai)(A_i)からできるだけ小さい数を取り除いてゆき、QQ個取り除けたら最大値-最小値を求める。

これは高々O(N){\mathcal O}(N)ステップで済むので、ひとつひとつのステップがO(N){\mathcal O}(N)O(NlogN){\mathcal O}(N \log N)くらいで実行できればよい。

小さい数を取り除いていく具体的な手順を与えるために、上のステップ3について考えてみる。A1=Ap,A2=AqA'_1 = A_p, A'_2 = A_qとする。

A1,A2,...,Ap1  Ap+1,Ap+2,...,Aq1  Aq+1,Aq+2,...,ANA_1, A_2, ..., A_{p-1} \ | \ A_{p+1}, A_{p+2}, ..., A_{q-1} \ | \ A_{q+1}, A_{q+2},..., A_N

ApA_p, AqA_qを残したまま数を取り除いていくというのは、上の数列の縦線をまたがないように連続したKK個を選んで、その最小値を除いていくということにほかならない。なぜならAp,AqA_p, A_qはほかの数より小さいので、縦線をまたぐとAp,AqA_p, A_qが取り除かれてしまうからだ。つまり、(Ai)(A_i)を複数の独立した数列に分割してそれぞれの最小値を見ればよさそうだ。

さて、「複数の独立した数列」をどう管理すればよいかが問題である。上の3つの部分列をA1,A2,A3{\mathcal A_1}, {\mathcal A_2}, {\mathcal A_3}とする。これらの数列から小さい数を除いていくとき、数列の最小値と長さにしか興味がないので、A1,A2,A3{\mathcal A_1}, {\mathcal A_2}, {\mathcal A_3}はそれぞれ(昇順の)優先度付きキューで表すことができる。さらに、A1,A2,A3{\mathcal A_1}, {\mathcal A_2}, {\mathcal A_3}自身も優先度付きキューQ{\mathcal Q}にいれることにし、Q{\mathcal Q}の順序はAA:{\mathcal A} \le {\mathcal A}' : \Leftrightarrow A{\mathcal A}の最小値\leA{\mathcal A}'の最小値 とする。

一般に、ll個の部分列からQQ個の数を取り除く操作を書き下すと、次のようになる。

  • A1,A2,...,Al{\mathcal A_1}, {\mathcal A_2}, ..., {\mathcal A_l}の中で長さがKK以上であるものだけQ{\mathcal Q}に入れて、残りは捨てる。
  • 次のステップを最大QQ回繰り返す。
    1. Q{\mathcal Q}の先頭から数列A{\mathcal A}を取り出す。
    2. A{\mathcal A}の最小値を取り出す。
    3. A{\mathcal A}の長さがまだKK以上ならQ{\mathcal Q}に戻し、KK未満なら捨てる。
  • Q{\mathcal Q}が空になる前にQQ個の数を取り出せたなら、その最大値-最小値を求める。

以上で答えが求まる。全体の計算量はO(N2logN){\mathcal O}(N^2 \log N)


問題を読んだときはNNが小さいので何をやってもできそうな気がしたけど、具体的な手順を与えるのに膨大な時間をかけてしまった。