Lazy Segment Tree (data_structure/lazy_segment_tree.hpp)
- View this file on GitHub
- View document part on GitHub
- Last update: 2025-05-23 18:14:00+09:00
- Include:
#include "data_structure/lazy_segment_tree.hpp"
ac-libraryをそのまま持ってきただけです
遅延評価セグメントツリーは、区間に対する更新と区間に対するクエリを高速に行うデータ構造です。
使い方
コンストラクタ
LazySegmentTree()
LazySegmentTree(int n)
explicit LazySegmentTree(const std::vector<S>& v)
説明
Lazy Segment Tree を構築します。
-
LazySegmentTree()
: 要素数 0 の Lazy Segment Tree を構築します。 -
LazySegmentTree(int n)
: 要素数n
の Lazy Segment Tree を構築します。初期値は単位元e()
で埋められます。 -
LazySegmentTree(const std::vector<S>& v)
: ベクトルv
の要素で Lazy Segment Tree を構築します。 -
n
: 要素数。 -
v
: 初期値となる要素のベクトル。
計算量
$O(N)$ または $O( | v | )$ |
set
void set(int p, S x)
説明
位置 p
の要素を x
に更新します。
-
p
: 更新する要素のインデックス (0-indexed)。 -
x
: 更新後の値。
制約
$0 \le p < n$
計算量
$O(\log N)$
operator[]
S operator[](int p)
説明
位置 p
の要素の値を取得します。
-
p
: 取得する要素のインデックス (0-indexed)。 - 戻り値: 位置
p
の要素の値。
制約
$0 \le p < n$
計算量
$O(\log N)$
operator()
S operator()(int l, int r)
説明
区間 $[l, r)$ の要素に対する集約結果を取得します。
-
l
: 区間の開始インデックス (0-indexed, 含む)。 -
r
: 区間の終了インデックス (0-indexed, 含まない)。 - 戻り値: 区間 $[l, r)$ の要素に対する集約結果。
制約
$0 \le l \le r \le n$
計算量
$O(\log N)$
all_prod
S all_prod()
説明
区間 $[0, n)$ の要素に対する集約結果を取得します。
- 戻り値: 区間 $[0, n)$ の要素に対する集約結果。
計算量
$O(1)$
apply
void apply(int p, F f)
void apply(int l, int r, F f)
説明
-
apply(int p, F f)
: 位置p
の要素に作用素f
を適用します。 -
apply(int l, int r, F f)
: 区間 $[l, r)$ の各要素に作用素f
を適用します。 -
p
: 作用素を適用する要素のインデックス (0-indexed)。 -
l
: 作用素を適用する区間の開始インデックス (0-indexed, 含む)。 -
r
: 作用素を適用する区間の終了インデックス (0-indexed, 含まない)。 -
f
: 適用する作用素。
制約
-
apply(int p, F f)
: $0 \le p < n$ -
apply(int l, int r, F f)
: $0 \le l \le r \le n$
計算量
$O(\log N)$
max_right
int max_right(int l, auto g)
説明
区間 $[l, r)$ において、左端 l
から始めて条件 g
を満たす最大の r
を二分探索で求めます。つまり、$g(\text{op}(a_l, a_{l+1}, \dots, a_{r-1}))$ が真となる最大の r
を返します。
-
l
: 探索を開始する左端のインデックス (0-indexed)。 -
g
: 条件を表す関数オブジェクト。引数として集約結果を取り、bool を返します。g(e())
は常に真である必要があります。 - 戻り値: 条件を満たす最大の
r
。
制約
$0 \le l \le n$
計算量
$O(\log N)$
min_left
int min_left(int r, auto g)
説明
区間 $[l, r)$ において、右端 r
から始めて条件 g
を満たす最小の l
を二分探索で求めます。つまり、$g(\text{op}(a_l, a_{l+1}, \dots, a_{r-1}))$ が真となる最小の l
を返します。
-
r
: 探索を終了する右端のインデックス (0-indexed)。 -
g
: 条件を表す関数オブジェクト。引数として集約結果を取り、bool を返します。g(e())
は常に真である必要があります。 - 戻り値: 条件を満たす最小の
l
。
制約
$0 \le r \le n$
計算量
$O(\log N)$
Verified with
Code
#pragma once
#include <vector>
template <typename S, auto op, auto e, typename F, auto mapping, auto composition, auto id>
struct LazySegmentTree {
using _T = S;
using _F = F;
static constexpr auto _op = op;
static constexpr auto _e = e;
LazySegmentTree() : LazySegmentTree(0) {}
explicit LazySegmentTree(int n) : LazySegmentTree(std::vector<S>(n, e())) {}
explicit LazySegmentTree(const std::vector<S>& v) : n(static_cast<int>(v.size())) {
size = 1;
while (size < n) size *= 2;
log = 0;
while (!(size & (1 << log))) log++;
d = std::vector<S>(2 * size, e());
lz = std::vector<F>(size, id());
for (int i = 0; i < n; i++) d[size + i] = v[i];
for (int i = size - 1; i >= 1; i--) {
update(i);
}
}
void set(int p, S x) {
p += size;
for (int i = log; i >= 1; i--) push(p >> i);
d[p] = x;
for (int i = 1; i <= log; i++) update(p >> i);
}
S operator[](int p) {
p += size;
for (int i = log; i >= 1; i--) push(p >> i);
return d[p];
}
S operator()(int l, int r) {
if (l == r) return e();
l += size;
r += size;
for (int i = log; i >= 1; i--) {
if (((l >> i) << i) != l) push(l >> i);
if (((r >> i) << i) != r) push((r - 1) >> i);
}
S sml = e(), smr = e();
while (l < r) {
if (l & 1) sml = op(sml, d[l++]);
if (r & 1) smr = op(d[--r], smr);
l >>= 1;
r >>= 1;
}
return op(sml, smr);
}
S all_prod() {
return d[1];
}
void apply(int p, F f) {
p += size;
for (int i = log; i >= 1; i--) push(p >> i);
d[p] = mapping(f, d[p]);
for (int i = 1; i <= log; i++) update(p >> i);
}
void apply(int l, int r, F f) {
if (l == r) return;
l += size;
r += size;
for (int i = log; i >= 1; i--) {
if (((l >> i) << i) != l) push(l >> i);
if (((r >> i) << i) != r) push((r - 1) >> i);
}
{
int l2 = l, r2 = r;
while (l < r) {
if (l & 1) all_apply(l++, f);
if (r & 1) all_apply(--r, f);
l >>= 1;
r >>= 1;
}
l = l2;
r = r2;
}
for (int i = 1; i <= log; i++) {
if (((l >> i) << i) != l) update(l >> i);
if (((r >> i) << i) != r) update((r - 1) >> i);
}
}
int max_right(int l, auto g) {
assert(g(e()));
if (l == n) return n;
l += size;
for (int i = log; i >= 1; i--) push(l >> i);
S sm = e();
do {
while (l % 2 == 0) l >>= 1;
if (!g(op(sm, d[l]))) {
while (l < size) {
push(l);
l = (2 * l);
if (g(op(sm, d[l]))) {
sm = op(sm, d[l]);
l++;
}
}
return l - size;
}
sm = op(sm, d[l]);
l++;
} while ((l & -l) != l);
return n;
}
int min_left(int r, auto g) {
assert(g(e()));
if (r == 0) return 0;
r += size;
for (int i = log; i >= 1; i--) push((r - 1) >> i);
S sm = e();
do {
r--;
while (r > 1 && (r % 2)) r >>= 1;
if (!g(op(d[r], sm))) {
while (r < size) {
push(r);
r = (2 * r + 1);
if (g(op(d[r], sm))) {
sm = op(d[r], sm);
r--;
}
}
return r + 1 - size;
}
sm = op(d[r], sm);
} while ((r & -r) != r);
return 0;
}
int n, size, log;
std::vector<S> d;
std::vector<F> lz;
void update(int k) {
d[k] = op(d[2 * k], d[2 * k + 1]);
}
void all_apply(int k, F f) {
d[k] = mapping(f, d[k]);
if (k < size) lz[k] = composition(f, lz[k]);
}
void push(int k) {
all_apply(2 * k, lz[k]);
all_apply(2 * k + 1, lz[k]);
lz[k] = id();
}
};
#line 2 "data_structure/lazy_segment_tree.hpp"
#include <vector>
template <typename S, auto op, auto e, typename F, auto mapping, auto composition, auto id>
struct LazySegmentTree {
using _T = S;
using _F = F;
static constexpr auto _op = op;
static constexpr auto _e = e;
LazySegmentTree() : LazySegmentTree(0) {}
explicit LazySegmentTree(int n) : LazySegmentTree(std::vector<S>(n, e())) {}
explicit LazySegmentTree(const std::vector<S>& v) : n(static_cast<int>(v.size())) {
size = 1;
while (size < n) size *= 2;
log = 0;
while (!(size & (1 << log))) log++;
d = std::vector<S>(2 * size, e());
lz = std::vector<F>(size, id());
for (int i = 0; i < n; i++) d[size + i] = v[i];
for (int i = size - 1; i >= 1; i--) {
update(i);
}
}
void set(int p, S x) {
p += size;
for (int i = log; i >= 1; i--) push(p >> i);
d[p] = x;
for (int i = 1; i <= log; i++) update(p >> i);
}
S operator[](int p) {
p += size;
for (int i = log; i >= 1; i--) push(p >> i);
return d[p];
}
S operator()(int l, int r) {
if (l == r) return e();
l += size;
r += size;
for (int i = log; i >= 1; i--) {
if (((l >> i) << i) != l) push(l >> i);
if (((r >> i) << i) != r) push((r - 1) >> i);
}
S sml = e(), smr = e();
while (l < r) {
if (l & 1) sml = op(sml, d[l++]);
if (r & 1) smr = op(d[--r], smr);
l >>= 1;
r >>= 1;
}
return op(sml, smr);
}
S all_prod() {
return d[1];
}
void apply(int p, F f) {
p += size;
for (int i = log; i >= 1; i--) push(p >> i);
d[p] = mapping(f, d[p]);
for (int i = 1; i <= log; i++) update(p >> i);
}
void apply(int l, int r, F f) {
if (l == r) return;
l += size;
r += size;
for (int i = log; i >= 1; i--) {
if (((l >> i) << i) != l) push(l >> i);
if (((r >> i) << i) != r) push((r - 1) >> i);
}
{
int l2 = l, r2 = r;
while (l < r) {
if (l & 1) all_apply(l++, f);
if (r & 1) all_apply(--r, f);
l >>= 1;
r >>= 1;
}
l = l2;
r = r2;
}
for (int i = 1; i <= log; i++) {
if (((l >> i) << i) != l) update(l >> i);
if (((r >> i) << i) != r) update((r - 1) >> i);
}
}
int max_right(int l, auto g) {
assert(g(e()));
if (l == n) return n;
l += size;
for (int i = log; i >= 1; i--) push(l >> i);
S sm = e();
do {
while (l % 2 == 0) l >>= 1;
if (!g(op(sm, d[l]))) {
while (l < size) {
push(l);
l = (2 * l);
if (g(op(sm, d[l]))) {
sm = op(sm, d[l]);
l++;
}
}
return l - size;
}
sm = op(sm, d[l]);
l++;
} while ((l & -l) != l);
return n;
}
int min_left(int r, auto g) {
assert(g(e()));
if (r == 0) return 0;
r += size;
for (int i = log; i >= 1; i--) push((r - 1) >> i);
S sm = e();
do {
r--;
while (r > 1 && (r % 2)) r >>= 1;
if (!g(op(d[r], sm))) {
while (r < size) {
push(r);
r = (2 * r + 1);
if (g(op(d[r], sm))) {
sm = op(d[r], sm);
r--;
}
}
return r + 1 - size;
}
sm = op(d[r], sm);
} while ((r & -r) != r);
return 0;
}
int n, size, log;
std::vector<S> d;
std::vector<F> lz;
void update(int k) {
d[k] = op(d[2 * k], d[2 * k + 1]);
}
void all_apply(int k, F f) {
d[k] = mapping(f, d[k]);
if (k < size) lz[k] = composition(f, lz[k]);
}
void push(int k) {
all_apply(2 * k, lz[k]);
all_apply(2 * k + 1, lz[k]);
lz[k] = id();
}
};