遅延セグ木(ACL)の使い方
ACL(atcoder.lazysegtree)の遅延セグ木は、列の値をまとめる演算と、区間に一括で作用させる操作を自分で渡して使います。区間はすべて半開区間 $[l, r)$ で、各操作は $O(\log N)$ です。
LazySegTree(op, e, mapping, composition, id_, v) に渡すもの:
op(x, y): 値を 2 つまとめる結合的な演算。eはその単位元mapping(f, x): 作用 $f$ を値 $x$ に作用させた結果composition(f, g): 作用 $g$ のあとに $f$ をすること( $f \circ g$)を 1 つの作用にしたもの。 $f$ が新しく来た方id_: 何もしない作用v: 初期の列(または長さ $N$ を渡すとすべてe)
おもなメソッドは apply(l, r, f)(区間に作用)、apply(p, f)(1 点に作用)、prod(l, r)、all_prod()、get(p)、set(p, x)、max_right(l, g)、min_left(r, g) です。
和を求めるときは、足す量が区間の長さに比例するので、値を (和, 区間の長さ) の組にして、初期の列を [(x, 1) for x in a] とします。区間更新では、何もしない作用を None で表します。
以下のコードは、列 a から seg を作るところまでです。
区間加算・区間最小値
from atcoder.lazysegtree import LazySegTree
# 区間加算・区間最小値
INF = 1 << 60
op = min
e = INF
mapping = lambda f, x: f + x
composition = lambda f, g: f + g
id_ = 0
seg = LazySegTree(op, e, mapping, composition, id_, a)
# seg.apply(l, r, x): a[l:r] に x を足す / seg.prod(l, r): min(a[l:r])
区間加算・区間最大値
from atcoder.lazysegtree import LazySegTree
# 区間加算・区間最大値
INF = 1 << 60
op = max
e = -INF
mapping = lambda f, x: f + x
composition = lambda f, g: f + g
id_ = 0
seg = LazySegTree(op, e, mapping, composition, id_, a)
# seg.apply(l, r, x): a[l:r] に x を足す / seg.prod(l, r): max(a[l:r])
区間加算・区間和
from atcoder.lazysegtree import LazySegTree
# 区間加算・区間和。値は (和, 区間の長さ)
op = lambda x, y: (x[0] + y[0], x[1] + y[1])
e = (0, 0)
mapping = lambda f, x: (x[0] + f * x[1], x[1])
composition = lambda f, g: f + g
id_ = 0
seg = LazySegTree(op, e, mapping, composition, id_, [(x, 1) for x in a])
# seg.apply(l, r, x): a[l:r] に x を足す / seg.prod(l, r)[0]: sum(a[l:r])
区間更新・区間最小値
from atcoder.lazysegtree import LazySegTree
# 区間更新・区間最小値(作用 None は「何もしない」)
INF = 1 << 60
op = min
e = INF
mapping = lambda f, x: x if f is None else f
composition = lambda f, g: g if f is None else f
id_ = None
seg = LazySegTree(op, e, mapping, composition, id_, a)
# seg.apply(l, r, x): a[l:r] をすべて x にする / seg.prod(l, r): min(a[l:r])
区間更新・区間最大値
from atcoder.lazysegtree import LazySegTree
# 区間更新・区間最大値(作用 None は「何もしない」)
INF = 1 << 60
op = max
e = -INF
mapping = lambda f, x: x if f is None else f
composition = lambda f, g: g if f is None else f
id_ = None
seg = LazySegTree(op, e, mapping, composition, id_, a)
# seg.apply(l, r, x): a[l:r] をすべて x にする / seg.prod(l, r): max(a[l:r])
区間更新・区間和
from atcoder.lazysegtree import LazySegTree
# 区間更新・区間和。値は (和, 区間の長さ)、作用 None は「何もしない」
op = lambda x, y: (x[0] + y[0], x[1] + y[1])
e = (0, 0)
mapping = lambda f, x: x if f is None else (f * x[1], x[1])
composition = lambda f, g: g if f is None else f
id_ = None
seg = LazySegTree(op, e, mapping, composition, id_, [(x, 1) for x in a])
# seg.apply(l, r, x): a[l:r] をすべて x にする / seg.prod(l, r)[0]: sum(a[l:r])
区間アフィン変換・区間和
作用 $(b, c)$ は、区間の各値 $x$ を $bx + c$ にします。2 つの作用の合成は、 $x \mapsto b_2 (b_1 x + c_1) + c_2 = b_1 b_2 x + (b_2 c_1 + c_2)$ です。区間加算は $(1, c)$、区間更新は $(0, c)$ で表せるので、加算と更新が混ざるときにも使えます。
from atcoder.lazysegtree import LazySegTree
# 区間アフィン変換・区間和(mod)。値は (和, 区間の長さ)、作用 (b, c) は x を b x + c にする
mod = 998244353
op = lambda x, y: ((x[0] + y[0]) % mod, x[1] + y[1])
e = (0, 0)
mapping = lambda f, x: ((f[0] * x[0] + f[1] * x[1]) % mod, x[1])
composition = lambda f, g: (f[0] * g[0] % mod, (f[0] * g[1] + f[1]) % mod)
id_ = (1, 0)
seg = LazySegTree(op, e, mapping, composition, id_, [(x, 1) for x in a])
# seg.apply(l, r, (b, c)): a[l:r] の各 x を b x + c にする / seg.prod(l, r)[0]: sum(a[l:r]) % mod
# 区間加算は (1, c)、区間更新は (0, c)
区間加算・区間最小値とその個数
すべての値が $0$ 以上のとき、区間の $0$ の個数(最小値が $0$ のときの個数)を求めるのに使えます。
from atcoder.lazysegtree import LazySegTree
# 区間加算・区間最小値とその個数。値は (最小値, 個数)
INF = 1 << 60
def op(x, y):
if x[0] < y[0]:
return x
if x[0] > y[0]:
return y
return (x[0], x[1] + y[1])
e = (INF, 0)
mapping = lambda f, x: (x[0] + f, x[1])
composition = lambda f, g: f + g
id_ = 0
seg = LazySegTree(op, e, mapping, composition, id_, [(x, 1) for x in a])
# seg.apply(l, r, x): a[l:r] に x を足す / seg.prod(l, r): (min(a[l:r]), その値の個数)
0/1 の列の区間反転・区間の 1 の個数
from atcoder.lazysegtree import LazySegTree
# 0/1 の列の区間反転・区間の 1 の個数。値は (1 の個数, 区間の長さ)、作用は反転するかどうか
op = lambda x, y: (x[0] + y[0], x[1] + y[1])
e = (0, 0)
mapping = lambda f, x: (x[1] - x[0], x[1]) if f else x
composition = lambda f, g: f ^ g
id_ = False
seg = LazySegTree(op, e, mapping, composition, id_, [(x, 1) for x in a])
# seg.apply(l, r, True): a[l:r] の 0 と 1 を入れ替える / seg.prod(l, r)[0]: a[l:r] の 1 の個数