noshi91のメモ

データ構造のある風景

Range Mode Query 空間 Θ(n) 構築 Θ(n√n) クエリ Θ(√n)

概要

Range Mode Query - data-structures

これをもう少しだけ詳しめに解説します。 面白いので好きなアルゴリズムです。

アルゴリズム

まずは平方分割して、ブロックの境界を端点とする区間 ( \Theta(n) 個ある) の全てについて最頻値とその頻度を計算します。 他にも前計算するものがありますが、説明が難しいのでクエリの処理方法を追いながらやっていきます。

区間  \lbrack l, r) の最頻値を計算したいとします。 まずは前計算したものを使って、暫定的な最頻値と頻度 (  =: freq ) を取得します。 この暫定解が更新されるとすれば、それはブロックからはみ出た左右の端数の部分に含まれるいずれかの値によるものになります。 従って、端数の部分の全要素について、その要素が  \lbrack l, r) に出現する回数が  freq を超えるか判定して、適切に更新する、というアルゴリズムがまず思い浮かびます。

f:id:noshi91:20201026134939j:plain

クエリを  {\rm O}(\sqrt n) にしたいので、それぞれの要素について  {\rm O}(1) で頻度を取得したくなりますが、 {\rm O}(n) の空間計算量でこれは絶望的です。

そこで見る方向を変えて、「左側の端数部分に含まれる各要素  x について、その  freq 個後ろの  x の位置が  r より手前にあるか」という判定を考えます。 この判定自体は、全ての数についてその数だけ抜き出した index の列を持っておけば空間計算量  \Theta(n) 、時間計算量  \Theta(1) で可能です。 これを用いた「 r より手前にあったら  freq が更新されるということなので、 freq を 1 増やして繰り返す」というアルゴリズムは、 \Theta(\sqrt n) の時間計算量になります。 なぜなら、ステップを 1 つ進めるたびに「 x では更新されなくなったので、次の数に移る」「 freq が 1 増える」のいずれかが発生し、 freq は最初の暫定解から  \Theta(\sqrt n) しか増え得ないからです。 右側の端数部分に含まれる数については逆に、 freq 個手前の  x の index が  l より後ろにあるかを判定すれば全く同様です。