概要
木上の等高線集約クエリ - suisen のブログ この記事で未解決になっていた変種 を解決する。
を可換モノイドとし、 頂点の木 の各頂点には の元が書き込まれているとする。
- に書かれている値を に書き換える。
- から距離 以上 未満の頂点に書かれた値の総和を出力する。
以上のクエリを前計算 、 クエリ の時間計算量で処理できる。 ただし の演算は で可能とする。
また、同様の手法によって区間作用 点取得も処理できる。 JOI 2021/2022 春合宿 day3-2 がこの問題だが、時間計算量的に通るかどうかはよく分からない。
距離が辺の重み付きで定義される場合も前計算が かかるものの同様に処理できる。
アルゴリズム
概要で述べた記事の内容を概ね理解していることを前提とする。
から重心を取り除いたときに分かれる部分木たちを とする。 従来の手法で問題となっていたのは、重心の次数が になってしまい、ここの処理に時間がかかるという点である。 そこで、 を一度に重心に付ける 分木ではなく、 と対応しないノードを間に挟み二分木を作る。
上図において実線の三角形は の元であり、点線の長方形は に対応しない新たに追加されたノードである。 書き込まれた数は後述するアルゴリズムにおける重みである。
新たに追加されたノードについても、その子孫の頂点たちを深さ順に並べて Segment Tree で管理する。 また、 の元については従来の重心分解同様、再帰的に重心を取り除き木構造を作成し、得られた根付き木を とする。 この状態で更新クエリを処理することを考えると、 において から根に至るのパスの長さが になっているかどうかが問題となる。 の元の重みをその部分木の頂点数として を葉とする Huffman coding の木を作ることで、それが達成される。 具体的には以下のアルゴリズムを実行する。
- とする。
- の間、以下を繰り返す。
- から重みが最小及び 番目に小さい元を取り出し、それらを子とするノードを作成する。 作成されたノードの重みを子の重みの和と定義し、 に追加する。
において から根に至るパス の長さを考える。 の重み を、 において の子孫である の頂点の個数と定義する。 において の次に現れる の頂点を とすると、- パスの長さは 以下である。 これは の親を つ辿ると が 倍以上になるためであり、Huffman coding の木を作るアルゴリズムから証明できる。 において の頂点が現れる回数は重心分解自体の性質により であり、 の第 項の和は となる。 の第 項の和は telescoping sum の形であるから となる。
雑記
厳密な定式化はできていないが、領域木でできるクエリは全部できるのではないかと思う (形が同じなので)。 裏を返すと、一般の区間更新区間取得は難しそうに見える。