セグメント木です。
seg = Segtree.new(arg, e) { |x, y| ... }
第1引数は、Integer
またはArray
です。
- 第1引数が
Integer
のn
のとき、長さn
・初期値e
のセグメント木を作ります。 - 第1引数が長さ
n
のArray
のa
のとき、a
をもとにセグメント木を作ります。
第2引数は単位元e
で、ブロックで二項演算op(x, y)
を定義することで、モノイドを定義する必要があります。
計算量 O(n)
n = 10**5
inf = (1 << 60) - 1
Segtree.new(n, 0) { |x, y| x.gcd y } # gcd
Segtree.new(n, 1) { |x, y| x.lcm y } # lcm
Segtree.new(n, -inf) { |x, y| [x, y].max } # max
Segtree.new(n, inf) { |x, y| [x, y].min } # min
Segtree.new(n, 0) { |x, y| x | y } # or
Segtree.new(n, 1) { |x, y| x * y } # prod
Segtree.new(n, 0) { |x, y| x + y } # sum
seg.set(pos, x)
a[pos]
に x
を代入します。
計算量
O(logn)
seg.get(pos)
a[pos]
を返します。
計算量
O(1)
seg.prod(l, r)
op(a[l], ..., a[r - 1])
を返します。引数は半開区間です。l == r
のとき、単位元e
を返します。
制約
0 ≦ l ≦ r ≦ n
計算量
O(logn)
seg.all_prod
op(a[0], ..., a[n - 1])
を返します。空のセグメントツリー、つまりサイズが0のとき、単位元e
を返します。
計算量
O(1)
seg.max_right(l, &f)
Segtree上でl <= r <= n
の範囲で、f(prod(l, r))
の結果を二分探索をして条件に当てはまるr
を返します。
以下の条件を両方満たす r
(l <= r <= n
)を(いずれか一つ)返します。
r = l
もしくはf(prod(l, r))
がtrue
となるr
r = n
もしくはf(prod(l, r + 1))
がfalse
となるr
prod(l, r)
は半開区間[l, r)
であることに注意。
制約
f
を同じ引数で呼んだとき、返り値は同じ。f(e)
がtrue
0 ≦ l ≦ n
計算量
O(log n)
seg.min_left(r, &f)
Segtree上で0 <= l <= r
の範囲で、f(prod(l, r))
の結果を二分探索をして条件に当てはまるl
を返します。
以下の条件を両方満たす l
(0 <= l <= r
)を(いずれか一つ)返します。
l = r
もしくはf(prod(l, r))
がtrue
となるl
l = 0
もしくはf(prod(l - 1, r))
がfalse
となるl
prod(l, r)
は半開区間[l, r)
であることに注意。
制約
f
を同じ引数で呼んだとき、返り値は同じ。f(e)
がtrue
0 ≦ l ≦ n
計算量
O(log n)
-
- xorのセグメントツリーの基本的な典型問題です。FenwickTree(BIT)をxorに改造するだけでも解けます。
- ACコード(1538ms): 通常のSegtree解。
- ACコード(821ms): FenwickTree(BIT)のxor改造版。
- 当ライブラリ
- 当ライブラリの実装コード segtree.rb
- 当ライブラリのテストコード segtree.rb
- テストコードも具体的な使い方として役に立つかもしれまん。
- 本家ライブラリ
- セグメントツリーについて
基本的に、本家の実装に合わせています。
内部実装に関しても、変数@d
の0番目の要素には必ず単位元@e
が入ります。
Rubyにはp
メソッドがあるので、引数p
について、p
ではなくpositionのpos
を変数名として使いました。
同様に、本家の変数size
を、わかりやすさからleaf_size
としています。
本家C++ライブラリは独自定義のinternal::ceil_pow2
関数を用いてますが、本ライブラリではRuby既存のメソッドInteger#bit_length
を用いています。そちらの方が計測した結果、高速でした。