Sqrt Tree

SqrtTree は、静的なモノイド列を再帰的に平方分割し、区間積 query を高速に処理する data structure です。

この Nim-ACL 実装は、理解しやすい再帰版です。

AtCoder公式講座では、query 区間がどの level で分岐するかを bit 演算で直接求めることで、query を O(1) にする発展形も説明されています。

使いどころ

考え方

まず列をおよそ平方根個の block に分けます。

各 block について、

を前計算します。

query の両端が異なる block に入るなら、

  1. 左端から左 block の末尾
  2. 間にある完全な block
  3. 右 block の先頭から右端

の高々3部分に分解できます。

両端が同じ block にある場合は、その block をさらに平方分割した子 node へ進みます。

import

import atcoder/extra/structure/sqrt_tree

ローカル短縮 import:

import lib/structure/sqrt_tree

API

proc initSqrtTree[T](
  data: openArray[T],
  identity: T,
  op: proc(a, b: T): T
): SqrtTree[T]

proc prod(
  tree: SqrtTree[T],
  l, r: int
): T

proc allProd(tree: SqrtTree[T]): T

query 区間は非空の半開区間 [l, r) です。

使用例

proc add(a, b: int): int = a + blet tree = initSqrtTree(  @[1, 2, 3, 4, 5],  identity = 0,  op = add,)doAssert tree.prod(1, 4) == 9doAssert tree[(2, 5)] == 12

非可換演算にも使えます。

proc concat(a, b: string): string = a & blet tree = initSqrtTree(  @["a", "b", "c", "d"],  identity = "",  op = concat,)doAssert tree.prod(1, 4) == "bcd"

他の構造との使い分け

構造 前計算 query memory
Segment Tree O(n) O(log n) O(n)
Disjoint Sparse Table O(n log n) O(1) O(n log n)
この Sqrt Tree O(n log log n) O(log log n) O(n log log n)

注意

関連資料