Visualization of [Delayed propagation segment tree
Combine segment tree with range contraction and dual segment tree with range action
Here we use the implementation so far as possible, managing the "table of actions not yet applied (delayed) to the values" in a dual segment tree and the "values" separately in a segment tree
Initialize the value segment tree normally 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 |
- See [maspy's article](https://maspypy.com/segment-tree-%E3%81%AE%E3%81%8A%E5%8B%89%E5%BC%B72) for why propagation from both ends instead of the entire range is fine.
- [Range of influence of propagation downwards](/en/Range%20of%20influence%20of%20propagation%20downwards)
- [up operation](/en/up%20operation)
- In the following implementation, the propagation path is calculated first and used both up and down.
- Upward propagation merges in the middle, so this method is good because it eliminates the merging and beyond.
- [RMQ and RUQ (Lazy Evaluated Segment Tree) - yaketake08's Implementation Memo](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
Consider doing Range add, range sum
a * b and powers (^ n) are related by (a * b) ^ n = (a ^ n) * (b ^ n).
- Call it distributive law.a + b and (+ n).(a + b) + n = (a + n) + (b + n) notlambda (v1, s1), (v2, s2): (v1 + v2, s1 + s2)lambda (v, s): v + s * nThis page is auto-translated from /nishio/遅延伝搬セグメント木の可視化 using DeepL. If you looks something interesting but the auto-translated English is not good enough to understand it, feel free to let me know at @nishio_en. I'm very happy to spread my thought to non-Japanese readers.