Li-Chao (Segment) Tree with Lazy Propagation
概要
以下のクエリを処理できる遅延評価 Li Chao (Segment) Tree の実装
-
全ての \(a_i\) の初期値は \(+\infty\)
-
線分追加: \(a_l, ..., a_{r-1}\) の各 \(a_i\) について \(\min(a_i, a \cdot i + b)\) に更新
-
線分加算: \(a_l, ..., a_{r-1}\) の各 \(a_i\) について \(a_i + (a \cdot i + b)\) に更新
-
値取得: \(a_i\) の値を求める
実装説明
線分追加, 値取得については従来の Li-Chao Tree と同様。(pushdown等は行う)
線分加算クエリにおいて、ノードを探索している間に現在のノードに存在する線分 \(f(x) = ax + b\) の一部分を更新する必要がある場合は、ノードから \(f(x)\) を取り出し左右の子ノードのそれぞれに \(f(x)\) を線分追加することで線分を分割する。
一回の線分加算クエリでは、ノードが持つ \(f(x)\) を子ノードへの線分追加する処理を高々 \(O(\log N)\) 回行うことになり、一回の線分追加は \(O(\log N)\) であるため、一回の線分加算クエリは \(O(\log^2 N)\) となる。
計算量
区間 \([0, N)\) に対する各クエリについて
-
線分追加: \(O(\log^2 N)\)
-
線分加算: \(O(\log^2 N)\)
-
値取得: \(O(\log N)\)
実装
#include <algorithm>
using ll = long long;
using namespace std;
const static ll inf = 1e18L;
class LiChaoTree {
struct Line {
ll a, b;
Line(ll a, ll b) : a(a), b(b) {}
Line() : a(0), b(0) {}
inline ll f(ll x) const {
return a * x + b;
}
Line& operator+=(const Line &ln) {
if (b >= inf || ln.b >= inf) {
a = 0; b = inf;
} else {
a += ln.a;
b += ln.b;
}
return *this;
}
bool operator==(const Line &ln) const {
return a == ln.a && b == ln.b;
}
};
const Line line_inf = Line{0, inf};
const Line line_zero = Line{0, 0};
struct Node {
Node *left, *right;
Line line, lazy;
Node(Line line) : left(nullptr), right(nullptr), line(line) {}
};
Node *root;
ll xl, xr;
inline bool compare(Line &l0, Line &l1, ll x) const {
return l0.f(x) <= l1.f(x);
}
inline void _push_line(Node *nd, ll l, ll r) {
if (nd == nullptr || nd->line == line_inf) return;
ll m = (l + r) / 2;
nd->left = _add_line(nd->left, nd->line, l, m);
nd->right = _add_line(nd->right, nd->line, m, r);
nd->line = line_inf;
}
inline void _add_lazy(Node *nd, Line &line) {
if (nd == nullptr) return;
nd->line += line;
nd->lazy += line;
}
inline void _push_lazy(Node *nd, ll l, ll r) {
if (nd == nullptr || nd->lazy == line_zero) return;
_add_lazy(nd->left, nd->lazy);
_add_lazy(nd->right, nd->lazy);
nd->lazy = line_zero;
}
Node* _add_line(Node *nd, Line line, ll l, ll r) {
if (l == r) return nullptr;
ll m = (l + r) / 2;
if (nd == nullptr) return new Node(line);
_push_lazy(nd, l, r);
bool left = compare(line, nd->line, l);
bool mid = compare(line, nd->line, m);
bool right = compare(line, nd->line, r);
if (mid) {
swap(nd->line, line);
}
if(r-l > 1 && left != right) {
if (left != mid) {
nd->left = _add_line(nd->left, line, l, m);
} else {
nd->right = _add_line(nd->right, line, m, r);
}
}
return nd;
}
Node* _add_val(ll a, ll b, Node *nd, Line &line, ll l, ll r) {
if (r <= a || b <= l) {
return nd;
}
if (a <= l && r <= b) {
_add_lazy(nd, line);
return nd;
}
_push_lazy(nd, l, r);
_push_line(nd, l, r);
if (nd == nullptr) {
nd = new Node(line_inf);
}
ll m = (l + r) / 2;
nd->left = _add_val(a, b, nd->left, line, l, m);
nd->right = _add_val(a, b, nd->right, line, m, r);
return nd;
}
Node* _add_segment_line(ll a, ll b, Node *nd, Line &line, ll l, ll r) {
if (r <= a || b <= l) {
return nd;
}
if (a <= l && r <= b) {
return _add_line(nd, line, l, r);
}
_push_lazy(nd, l, r);
if (nd == nullptr) {
nd = new Node(line_inf);
}
ll m = (l + r) / 2;
nd->left = _add_segment_line(a, b, nd->left, line, l, m);
nd->right = _add_segment_line(a, b, nd->right, line, m, r);
return nd;
}
ll _query_min(ll k, ll l, ll r) {
Node *nd = root;
ll s = inf;
while (r - l > 0 && nd != nullptr) {
ll m = (l + r) / 2;
_push_lazy(nd, l, r);
s = min(s, nd->line.f(k));
if (k < m) {
r = m;
nd = nd->left;
} else {
l = m;
nd = nd->right;
}
}
return s;
}
public:
LiChaoTree(ll xl, ll xr) : xl(xl), xr(xr), root(nullptr) {}
// a_i <- min(a_i, a*i + b) for i in [xl, xr)
void add_line(ll a, ll b) {
Line line = Line{a, b};
root = _add_line(root, line, xl, xr);
}
// a_i <- min(a_i, a*i + b) for i in [l, r)
void add_segment_line(ll a, ll b, ll l, ll r) {
Line line = Line{a, b};
root = _add_segment_line(l, r, root, line, xl, xr);
}
// a_i <- a_i + (a*i + b) for i in [l, r)
void add_val(ll a, ll b, ll l, ll r) {
Line line = Line{a, b};
root = _add_val(l, r, root, line, xl, xr);
}
// a_i <- +∞ for i in [l, r)
void reset_val(ll l, ll r) {
Line line = line_inf;
root = _add_val(l, r, root, line, xl, xr);
}
// get a_i
ll query_min(ll x) {
return _query_min(x, xl, xr);
}
};