遅延伝搬セグメント木の可視化
範囲縮約のできるセグメント木と、範囲作用のできる双対セグメント木を組み合わせる
ここでは「まだ値に適用されてない(遅延された)作用のテーブル」を双対セグメント木で、「値」をセグメント木で別々に管理して可能な限りここまでの実装を使う
値のセグメント木を普通に初期化する python
>>> set_depth(4)
>>> value_table = [""] * SEGTREE_SIZE
>>> set_items(value_table, [chr(i + ord("a")) for i in range(8)])
>>> full_up(value_table, lambda x, y: f"{x}{y}")
>>> debugprint(value_table)
| abcdefgh |
| abcd | efgh |
| ab| cd| ef| gh|
|a|b|c|d|e|f|g|h|
>>> action_unity = PowAction(1)
>>> action_table = [action_unity] * SEGTREE_SIZE
>>> debugprint(action_table)
| ^1 |
| ^1 | ^1 |
| ^1 | ^1 | ^1 | ^1 |
|^1|^1|^1|^1|^1|^1|^1|^1|
(^n) + (^m) = (^(n*m))という合成ができる
pythondef action_composite(new_action, old_action):
return PowAction(new_action.value * old_action.value)
def action_force(action, value): ...def combined_action(new_action):
def f(args):
action, value = args
return (
action_composite(new_action, action),
action_force(new_action, value))
return f
python
>>> range_update(combined_table, L, R, combined_action(PowAction(2)))
>>> debugprint(action_table, 3)
| ^1 |
| ^2 | ^1 |
| ^1 | ^1 | ^2 | ^1 |
| ^1| ^1| ^1| ^1| ^1| ^1| ^1| ^1|
>>> debugprint(value_table, 3)
| abcdefgh |
| (abcd)^2 | efgh |
| ab | cd | (ef)^2| gh |
| a | b | c | d | e | f | g | h |
>>> up_propagate(value_table, up(L), lambda x, y: f"{x}{y}")
>>> up_propagate(value_table, up(R), lambda x, y: f"{x}{y}")
>>> debugprint(value_table, 3)
| (abcd)^2(ef)^2gh |
| (abcd)^2 | (ef)^2gh |
| ab | cd | (ef)^2| gh |
| a | b | c | d | e | f | g | h |
- なぜ範囲全体ではなく両端からの伝播でいいかは[maspyさんの記事](https://maspypy.com/segment-tree-%E3%81%AE%E3%81%8A%E5%8B%89%E5%BC%B72)を参照
- [下への伝搬の影響範囲](/ja/%E4%B8%8B%E3%81%B8%E3%81%AE%E4%BC%9D%E6%90%AC%E3%81%AE%E5%BD%B1%E9%9F%BF%E7%AF%84%E5%9B%B2)
- [up演算](/ja/up%E6%BC%94%E7%AE%97)
- なお下記の実装だと伝搬経路を先に計算して上にも下にも使いまわしている
- 上方向の伝搬は途中で合流するので、この方法は合流以降が省けてて良い
- [RMQ and RUQ (遅延評価セグメント木) - yaketake08's 実装メモ](https://tjkendev.github.io/procon-library/python/range_query/rmq_ruq_segment_tree_lp.html)
python
def down_propagate_force(table, pos, action_composite, action_force, action_unity):
max_level = pos.bit_length() - 1
for level in range(max_level):
i = pos >> (max_level - level)
action, value = table[i]
a, v = table[i * 2]
table[i * 2] = (
action_composite(action, a),
action_force(action, v))
a, v = table[i * 2 + 1]
table[i * 2 + 1] = (
action_composite(action, a),
action_force(action, v))
table[i] = (action_unity, value)
>>> L = 1
>>> R = 5
>>> down_propagate_force(
... combined_table, up(L),
... action_composite, action_force, action_unity)
>>> down_propagate_force(
... combined_table, up(R),
... action_composite, action_force, action_unity)
>>> debugprint(action_table)
| ^1 |
| ^1 | ^1 |
| ^1 | ^2 | ^1 | ^1 |
|^2|^2|^1|^1|^2|^2|^1|^1|
>>> debugprint(value_table)
| (abcd)^2(ef)^2gh |
| (abcd)^2 | (ef)^2gh |
| (ab)^2| (cd)^2| (ef)^2| gh |
|a^2|b^2| c | d |e^2|f^2| g | h |
>>> range_update(combined_table, L, R, combined_action(
... PowAction(3), action_composite, action_force))
>>> debugprint(action_table, 3)
| ^1 |
| ^1 | ^1 |
| ^1 | ^6 | ^1 | ^1 |
| ^2| ^6| ^1| ^1| ^6| ^2| ^1| ^1|
>>> debugprint(value_table, 3)
| (abcd)^2(ef)^2gh |
| (abcd)^2 | (ef)^2gh |
| (ab)^2 | ((cd)^2)^3 | (ef)^2 | gh |
| a^2 |(b^2)^3| c | d |(e^2)^3| f^2 | g | h |
>>> up_propagate(value_table, up(L), lambda x, y: f"{x}{y}")
>>> up_propagate(value_table, up(R), lambda x, y: f"{x}{y}")
>>> debugprint(value_table, 3)
| a^2(b^2)^3((cd)^2)^3(e^2)^3f^2gh |
| a^2(b^2)^3((cd)^2)^3 | (e^2)^3f^2gh |
| a^2(b^2)^3 | ((cd)^2)^3 | (e^2)^3f^2 | gh |
| a^2 |(b^2)^3| c | d |(e^2)^3| f^2 | g | h |
>>> L = 3
>>> R = 5
>>> down_propagate_force(
... combined_table, up(L),
... action_composite, action_force, action_unity)
>>> down_propagate_force(
... combined_table, up(R),
... action_composite, action_force, action_unity)
>>> debugprint(value_table)
| a^2(b^2)^3((cd)^2)^3(e^2)^3f^2gh |
| a^2(b^2)^3((cd)^2)^3 | (e^2)^3f^2gh |
| a^2(b^2)^3 | ((cd)^2)^3 | (e^2)^3f^2 | gh |
| a^2 |(b^2)^3| c^6 | d^6 |(e^2)^3| f^2 | g | h |
>>> value_unity = ""
>>> print(range_reduce(value_table, L, R, lambda x, y: f"{x}{y}", value_unity))
d^6(e^2)^3
Range add, range sumをやることを考える
a * bと累乗(^ n)は(a * b) ^ n = (a ^ n) * (b ^ n)の関係が成り立つa + b と(+ n)の間に分配法則が成り立たない(a + b) + n = (a + n) + (b + n) ではないlambda (v1, s1), (v2, s2): (v1 + v2, s1 + s2)lambda (v, s): v + s * n