区間クエリ系、第四弾。Lazy Segment Tree(通称:遅延セグ木)です。
仕組みについては、外部記事をたくさん読んでください。めちゃめちゃむずいです。
通常の Segment Tree では更新が1点のみしかできませんが、Lazy Segment Tree では区間の更新が Segment Tree と同等の計算量でできます。
そのほか初期化や、区間クエリに対する計算量は Segment Tree と同じです。
スクリプトはこちら。長いです。
class LazySegmentationTree: def __init__(self, l, e_ss, op_ss, e_sl, op_sl, op_ll, propagater): n = len(l) assert n > 1 and n & (n - 1) == 0 self.n = n self.depth = int.bit_length(n) self.e_ss = e_ss self.op_ss = op_ss self.seg = [e_ss for _ in range(2 * n)] self.e_sl = e_sl self.op_sl = op_sl self.op_ll = op_ll self.lazy = [e_sl for _ in range(2 * n)] self.propagater = propagater for i, li in enumerate(l): self.seg[i + n] = li for i in range(n - 1, 0, -1): self.seg[i] = self.op_ss(self.seg[2 * i], self.seg[2 * i + 1]) def _propagate(self, left, right): for i in range(self.depth - 1, 0, -1): l = left >> i r = right >> i propagetee = self.propagater(self.lazy[l]) ll = 2 * l lr = ll + 1 self.lazy[ll] = self.op_ll(self.lazy[ll], propagetee) self.lazy[lr] = self.op_ll(self.lazy[lr], propagetee) self.lazy[l] = self.e_sl if l == r: continue propagetee = self.propagater(self.lazy[r]) rl = 2 * r rr = rl + 1 self.lazy[rl] = self.op_ll(self.lazy[rl], propagetee) self.lazy[rr] = self.op_ll(self.lazy[rr], propagetee) self.lazy[r] = self.e_sl def _calc(self, left, right): for i in range(1, self.depth): l = left >> i r = right >> i ll = 2 * l lr = ll + 1 self.seg[l] = self.op_ss( self.op_sl(self.seg[ll], self.lazy[ll]), self.op_sl(self.seg[lr], self.lazy[lr]) ) if l == r: continue rl = 2 * r rr = rl + 1 self.seg[r] = self.op_ss( self.op_sl(self.seg[rl], self.lazy[rl]), self.op_sl(self.seg[rr], self.lazy[rr]) ) def _operate(self, left, right, v): for _ in range(self.depth - 1): if left & 1: self.lazy[left] = self.op_ll(self.lazy[left], v) left += 1 if right & 1: right -= 1 self.lazy[right] = self.op_ll(self.lazy[right], v) v = self.op_ll(v, v) left >>= 1 right >>= 1 if left == right: break def _query(self, left, right): res_l, res_r = self.e_ss, self.e_ss for _ in range(self.depth - 1): if left & 1: res_l = self.op_ss( res_l, self.op_sl(self.seg[left], self.lazy[left]) ) left += 1 if right & 1: right -= 1 res_r = self.op_ss( self.op_sl(self.seg[right], self.lazy[right]), res_r ) left >>= 1 right >>= 1 if left == right: break return self.op_ss(res_l, res_r) def operate(self, left, right, v): assert 0 <= left < self.n and 0 < right <= self.n and left < right left += self.n right += self.n self._propagate(left, right) self._operate(left, right, v) self._calc(left, right) def query(self, left, right): assert 0 <= left < self.n and 0 < right <= self.n and left < right left += self.n right += self.n self._propagate(left, right) self._calc(left, right) return self._query(left, right)
ACL Practice Contest で L - Lazy Segment Tree を通した時の提出回答がこちら。なんかコメントで「解けてない」とか言ってますけど、解けてます。
使ってみた所感としては、PyPy じゃないと間に合わないとか、個々の問題に合わせてチューニングが必要かもしれないとかありますので、ぜひお使いの際はカスタマイズしてお使いください。