Persistent Segment Tree(永続セグメント木)
1個の木がすべてのノードを管理し、PersistentVersionが各時点の
根を表します。
数列をコピーするときは要素を複製せず、versionを代入します。
versions[x] = versions[y]
初期化
一般形は次のとおりです。
type Tree =
PersistentSegTreeType[S](
op,
e,
)
var tree =
Tree.init(
valuesOrLength,
expectedUpdates = 0,
)
S: 要素と区間積の型op(left, right): 左区間と右区間の値を結合する関数e():opの単位元を返す関数valuesOrLength: 初期列、または初期長expectedUpdates: 予定する一点更新回数
opとeの加算モノイドの具体例は次のとおりです。
proc op(
left,
right: int,
): int =
left + right
proc e(): int =
0
このop, eを一般形へ渡します。
type SumTree =
PersistentSegTreeType[int](
op,
e,
)
var tree =
SumTree.init(
n,
expectedUpdates = q,
)
長さだけを渡すと、すべての要素をe()で初期化します。
簡略constructorも使用できます。
var tree =
initPersistentSegmentTree(
n,
op,
e,
expectedUpdates = q,
)
初期version
let version =
tree.initialVersion
versionは軽量なroot番号です。
一点更新
一般形は次のとおりです。
newVersion =
tree.set(
version,
position,
value,
)
version: 更新元のversionposition: 更新する添字value: 新しい値- 返り値: 更新後の新しいversion
元のversionは変更されません。
versions[x] =
tree.set(
versions[x],
y,
z,
)
短縮記法では、渡したversion変数を新しいversionへ更新します。
tree[version][position] = value
または、
tree[version, position] = value
一点取得
一般形は次のとおりです。
value =
tree.get(
version,
position,
)
version: 読み取り対象のversionposition: 読み取る添字
短縮記法は次のとおりです。
value = tree[version][position]
または、
value = tree[version, position]
区間積
一般形は次のとおりです。
answer =
tree.prod(
version,
range,
)
version: 読み取り対象のversionrange: 集約する範囲
半開区間の端点を直接渡すこともできます。
answer =
tree.prod(
version,
left,
right,
)
短縮記法は次のとおりです。
answer = tree[version][left ..< right]
または、
answer = tree[version, left ..< right]
全体積
answer =
tree.allProd(
version
)
配列への復元
values =
tree.toSeq(
version
)
計算量はO(n log n)です。主にdebug用途です。
version view
tree[version]はtreeとversionを参照する一時的なviewです。
tree[version][position] = value
answer = tree[version][range]
viewを変数へ保存することもできますが、元のtreeとversionより長く 保持しないでください。
expectedUpdates
- expectedUpdates: 意味 — 将来行う一点更新回数の見積もりです。
- expectedUpdates: 既定値 — 省略時は
0です。 - expectedUpdates: 正しさ — 値は計算結果や作成可能なversion数に影響しません。
- expectedUpdates: 小さすぎても — node storageが自動拡張され、結果は正しいままです。
- expectedUpdates: 大きすぎると — 余分なcapacityを事前確保します。
- expectedUpdates: メモリ — 初期node capacityの事前確保にだけ使います。
- 木の高さを
hとすると、一点更新ごとに正確にh + 1個のnodeを作ります。 - 初期capacityは概ね
2 * size + expectedUpdates * (h + 1)です。
raw static tuple形式
PersistentSegTree[S, (op: ..., e: ...)]は低水準APIです。
同じsignatureを持つ複数の匿名procをraw static tupleへ直接渡すと、 Nimが後のprocにも最初のprocを再利用する場合があります。
通常はPersistentSegTreeTypeまたは
initPersistentSegmentTreeを使用してください。
計算量
- 初期化:
O(n) - versionのコピー:
O(1) - 一点更新:
O(log n)時間・O(log n)追加memory - 一点取得:
O(log n) - 区間積:
O(log n) - 全体積:
O(1)