セグメント木の概念を、単純なセグメント木だけではなく双対セグメント木や遅延伝搬セグメント木まで通じて整理したい
下記ではセグメント木、双対セグメント木、遅延伝搬セグメント木、までを実装してAOJのテストケースでベリファイしているが、遅延伝搬セグメント木に関してはTLEしていて、柔軟な設計のせいでTLEしてるのかアルゴリズム的に何か間違えてるのかはまだ識別できてない
IDはこういう順番になっている python
>>> debugprint(range(SEGTREE_SIZE))
| 1 |
| 2 | 3 |
| 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|
配列が与えられたときに隣り合うものに二項演算をかけながらO(N)で構築できる
>>> table = [None] * SEGTREE_SIZE
>>> set_items(table, range(16))
>>> full_up(table, lambda x, y: f"{x}+{y}")
>>> debugprint(table, 3)
| 0+1+2+3+4+5+6+7+8+9+10+11+12+13+14+15 |
| 0+1+2+3+4+5+6+7 | 8+9+10+11+12+13+14+15 |
| 0+1+2+3 | 4+5+6+7 | 8+9+10+11 | 12+13+14+15 |
| 0+1 | 2+3 | 4+5 | 6+7 | 8+9 | 10+11 | 12+13 | 14+15 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15|
点更新
>>> point_update(table, 3, lambda x: f"f({x})")
>>> debugprint(table, 4)
| f(0+1+2+3+4+5+6+7+8+9+10+11+12+13+14+15) |
| f(0+1+2+3+4+5+6+7) | 8+9+10+11+12+13+14+15 |
| f(0+1+2+3) | 4+5+6+7 | 8+9+10+11 | 12+13+14+15 |
| 0+1 | f(2+3) | 4+5 | 6+7 | 8+9 | 10+11 | 12+13 | 14+15 |
| 0 | 1 | 2 |f(3)| 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
別のパターン
>>> point_set(table, 3, "x", lambda x, y: f"{x}+{y}")
>>> debugprint(table, 3)
| 0+1+2+x+4+5+6+7+8+9+10+11+12+13+14+15 |
| 0+1+2+x+4+5+6+7 | 8+9+10+11+12+13+14+15 |
| 0+1+2+x | 4+5+6+7 | 8+9+10+11 | 12+13+14+15 |
| 0+1 | 2+x | 4+5 | 6+7 | 8+9 | 10+11 | 12+13 | 14+15 |
| 0 | 1 | 2 | x | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15|
max二項演算を上に伝搬していくデモ python
# Point update, range max
>>> table = [0] * SEGTREE_SIZE
>>> set_items(table, range(16))
>>> full_up(table, max)
>>> debugprint(table)
| 15 |
| 7 | 15 |
| 3 | 7 | 11 | 15 |
| 1 | 3 | 5 | 7 | 9 | 11 | 13 | 15 |
|0 |1 |2 |3 |4 |5 |6 |7 |8 |9 |10|11|12|13|14|15|
>>> set_item(table, 5, 99)
>>> debugprint(table)
| 15 |
| 7 | 15 |
| 3 | 7 | 11 | 15 |
| 1 | 3 | 5 | 7 | 9 | 11 | 13 | 15 |
|0 |1 |2 |3 |4 |99|6 |7 |8 |9 |10|11|12|13|14|15|
>>> up_propagate_from_leaf(table, 5, max)
>>> debugprint(table)
| 99 |
| 99 | 15 |
| 3 | 99 | 11 | 15 |
| 1 | 3 | 99 | 7 | 9 | 11 | 13 | 15 |
|0 |1 |2 |3 |4 |99|6 |7 |8 |9 |10|11|12|13|14|15|
範囲縮約
python
>>> set_items(table, range(16))
>>> full_up(table, lambda x, y: f"{x}+{y}")
>>> range_reduce(table, 3, 11, lambda x, y: f"{x}+{y}", unity="0")
'0+3+4+5+6+7+8+9+10+0'
# Bin-op must be associative
>>> range_reduce(table, 3, 11, lambda x, y: f"({x}+{y})", unity="0")
'(((0+3)+4+5+6+7)+(8+9+(10+0)))'
点更新と範囲縮約の組み合わせで点更新と範囲和ができる python
>>> table = [0] * SEGTREE_SIZE
>>> set_items(table, range(16))
>>> full_up(table, lambda x, y: f"{x}+{y}")
>>> debugprint(table)
| 0+1+2+3+4+5+6+7+8+9+10+11+12+13+14+15 |
| 0+1+2+3+4+5+6+7 | 8+9+10+11+12+13+14+15 |
| 0+1+2+3 | 4+5+6+7 | 8+9+10+11 |12+13+14+15|
| 0+1 | 2+3 | 4+5 | 6+7 | 8+9 |10+11|12+13|14+15|
|0 |1 |2 |3 |4 |5 |6 |7 |8 |9 |10|11|12|13|14|15|
>>> point_update(table, 5, lambda x: f"{x}+99")
>>> debugprint(table)
| 0+1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+99 |
| 0+1+2+3+4+5+6+7+99 | 8+9+10+11+12+13+14+15 |
| 0+1+2+3 | 4+5+6+7+99 | 8+9+10+11 | 12+13+14+15 |
| 0+1 | 2+3 | 4+5+99 | 6+7 | 8+9 | 10+11 | 12+13 | 14+15 |
| 0 | 1 | 2 | 3 | 4 |5+99| 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
>>> range_reduce(table, 3, 11, lambda x, y: f"{x}+{y}", "0")
'0+3+4+5+6+7+99+8+9+10+0'
範囲更新
>>> table = [""] * SEGTREE_SIZE
>>> set_items(table, range(16))
>>> range_update(table, 1, 11, lambda x: f"f")
>>> debugprint(table)
| |
| | |
| | f | | |
| | f | | | f | | | |
|0 |f |2 |3 |4 |5 |6 |7 |8 |9 |f |11|12|13|14|15|
>>> range_update(table, 3, 15, lambda x: f"{x}g")
>>> debugprint(table)
| |
| | |
| | fg | g | |
| | f | | | f | | g | |
| 0 | f | 2 | 3g| 4 | 5 | 6 | 7 | 8 | 9 | f | 11| 12| 13|14g| 15|
>>> table = [0] * SEGTREE_SIZE
>>> range_update(table, 1, 11, lambda x: x + 1)
>>> debugprint(table, maxsize=4)
| 0 |
| 0 | 0 |
| 0 | 1 | 0 | 0 |
| 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
|0|1|0|0|0|0|0|0|0|0|1|0|0|0|0|0|
>>> range_update(table, 3, 15, lambda x: x + 2)
>>> debugprint(table, maxsize=4)
| 0 |
| 0 | 0 |
| 0 | 3 | 2 | 0 |
| 0 | 1 | 0 | 0 | 1 | 0 | 2 | 0 |
|0|1|0|2|0|0|0|0|0|0|1|0|0|0|2|0|
>>> down_propagate_to_leaf(table, 5, add, 0)
>>> debugprint(table, maxsize=4)
| 0 |
| 0 | 0 |
| 0 | 0 | 2 | 0 |
| 0 | 1 | 0 | 3 | 1 | 0 | 2 | 0 |
|0|1|0|2|3|3|0|0|0|0|1|0|0|0|2|0|
>>> down_propagate_to_leaf(table, 9, add, 0)
>>> debugprint(table, maxsize=4)
| 0 |
| 0 | 0 |
| 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 3 | 0 | 2 | 2 | 0 |
|0|1|0|2|3|3|0|0|3|3|1|0|0|0|2|0|
Verified
def main():
N, Q = map(int, input().split())
depth = N.bit_length() + 1
set_depth(depth)
table = [INF] * SEGTREE_SIZE
for _q in range(Q):
q, x, y = map(int, input().split())
if q == 0:
# update
point_set(table, x, y, min)
else:
# find
print(range_reduce(table, x, y + 1, min, INF))
def main():
from operator import add
N, Q = map(int, input().split())
depth = N.bit_length() + 1
set_depth(depth)
table = [0] * SEGTREE_SIZE
for _q in range(Q):
q, x, y = map(int, input().split())
if q == 0:
# add
point_update(table, x, lambda x: x + y)
else:
# sum
print(range_reduce(table, x, y + 1, add, 0))
def main():
from operator import add
N, Q = map(int, input().split())
depth = N.bit_length() + 1
set_depth(depth)
table = [0] * SEGTREE_SIZE
for time in range(Q):
q, *args = map(int, input().split())
if q == 0:
# update
s, t, value = args
range_update(table, s, t + 1, lambda x: x + value)
else:
# find
print(down_propagate_to_leaf(
table, args[0], add, 0))