Li Chao tree
(include/emthrm/dynamic_programming/li_chao_tree.hpp)
convex hull trick に対して、傾きに単調性のない直線または線分の追加を可能にするセグメント木である。
時間計算量
|
時間計算量 |
初期化 |
$O(N \log{N})$ |
直線の追加クエリ |
$O(\log{N})$ |
線分の追加クエリ |
$O((\log{N})^2)$ |
解答クエリ |
$O(\log{N})$ |
仕様
template <typename T, bool IS_MINIMIZED = true>
struct LiChaoTree;
T
-
IS_MINIMIZED
:最小化するかを表す変数
メンバ関数
名前 |
効果・戻り値 |
要件 |
explicit LiChaoTree(const std::vector<T>& xs_, const T inf); |
解答クエリの $x$ 座標の集合を $\mathrm{xs}$ としてオブジェクトを構築する。 |
|
void add(T a, T b); |
直線 $f(x) = ax + b$ を追加する。 |
|
void add(T a, T b, T left, T right); |
線分 $f(x) = ax + b$ ($\mathrm{left} \leq x < \mathrm{right}$) を追加する。 |
|
T query(const T x) const; |
$\min \text{/} \max \lbrace f(x) \mid f \in L \rbrace$ |
オフライン |
メンバ型
メンバ変数
メンバ関数
名前 |
効果・戻り値 |
explicit Line(const T a, const T b); |
直線 $f(x) = ax + b$ を表すオブジェクトを構築する。 |
T f(const T x) const; |
$f(x)$ |
参考文献
- https://smijake3.hatenablog.com/entry/2018/06/16/144548
https://lumakernel.github.io/ecasdqina/dynamic-programming/convex-hull-trick/LiChaoTree
- https://ei1333.github.io/luzhiled/snippets/structure/li-chao-tree.html
TODO
- https://twitter.com/hirakich1310354/status/1186193990841847810
- 線分の追加クエリの高速化
- https://smijake3.hatenablog.com/entry/2018/06/16/144548
- 削除可能 Li Chao tree
- https://sotanishy.github.io/cp-library-cpp/data-structure/cht/undoable_li_chao_tree.cpp
- 動的 Li Chao tree
- http://kazuma8128.hatenablog.com/entry/2018/02/28/102130
- https://github.com/ei1333/library/blob/master/structure/dynamic-li-chao-tree.cpp
- https://github.com/beet-aizu/library/blob/master/datastructure/nonmonotonicconvexhulltrick.cpp
https://lumakernel.github.io/ecasdqina/dynamic-programming/convex-hull-trick/DynamicLiChaoTree
- https://github.com/drken1215/algorithm/blob/master/DP/convex_hull_trick.cpp
- https://atcoder.jp/contests/colopl2018-final-open/submissions/3219122
- https://github.com/satanic0258/Cpp_snippet/blob/master/src/technique/ConvexHullTrick.cpp
- https://tjkendev.github.io/procon-library/cpp/convex_hull_trick/li_chao_tree_dynamic.html
- 永続動的 Li Chao tree
- https://github.com/ei1333/library/blob/master/structure/persistent-dynamic-li-chao-tree.cpp
https://lumakernel.github.io/ecasdqina/dynamic-programming/convex-hull-trick/DynamicPersistentLiChaoTree
Submissons
Verified with
Code
#ifndef EMTHRM_DYNAMIC_PROGRAMMING_LI_CHAO_TREE_HPP_
#define EMTHRM_DYNAMIC_PROGRAMMING_LI_CHAO_TREE_HPP_
#include <algorithm>
#include <bit>
#include <cassert>
#include <iterator>
#include <numeric>
#include <utility>
#include <vector>
namespace emthrm {
template <typename T, bool IS_MINIMIZED = true>
struct LiChaoTree {
struct Line {
T a, b;
explicit Line(const T a, const T b) : a(a), b(b) {}
T f(const T x) const { return a * x + b; }
};
explicit LiChaoTree(const std::vector<T>& xs_, const T inf) : n(1), xs(xs_) {
std::sort(xs.begin(), xs.end());
xs.erase(std::unique(xs.begin(), xs.end()), xs.end());
assert(xs.size() > 0);
n = std::bit_ceil(xs.size());
const T xs_back = xs.back();
xs.resize(n, xs_back);
dat.assign(n << 1, Line(0, inf));
}
void add(T a, T b) {
if constexpr (!IS_MINIMIZED) {
a = -a;
b = -b;
}
Line line(a, b);
add(&line, 1, 0, n);
}
void add(T a, T b, T left, T right) {
if constexpr (!IS_MINIMIZED) {
a = -a;
b = -b;
}
for (int len = 1,
node_l = std::distance(
xs.begin(), std::lower_bound(xs.begin(), xs.end(), left)),
node_r = std::distance(
xs.begin(), std::lower_bound(xs.begin(), xs.end(), right)),
l = node_l + n, r = node_r + n;
l < r;
l >>= 1, r >>= 1, len <<= 1) {
if (l & 1) {
Line line(a, b);
add(&line, l++, node_l, node_l + len);
node_l += len;
}
if (r & 1) {
Line line(a, b);
node_r -= len;
add(&line, --r, node_r, node_r + len);
}
}
}
T query(const T x) const {
int node = n + std::distance(xs.begin(),
std::lower_bound(xs.begin(), xs.end(), x));
T res = dat[node].f(x);
while (node >>= 1) {
if (dat[node].f(x) < res) res = dat[node].f(x);
}
return IS_MINIMIZED ? res : -res;
}
private:
int n;
std::vector<T> xs;
std::vector<Line> dat;
void add(Line* line, int node, int left, int right) {
const bool flag_l = dat[node].f(xs[left]) <= line->f(xs[left]);
const bool flag_r = dat[node].f(xs[right - 1]) <= line->f(xs[right - 1]);
if (flag_l && flag_r) return;
if (!flag_l && !flag_r) {
std::swap(dat[node], *line);
return;
}
const int mid = std::midpoint(left, right);
if (line->f(xs[mid]) < dat[node].f(xs[mid])) std::swap(dat[node], *line);
if (line->f(xs[left]) <= dat[node].f(xs[left])) {
add(line, node << 1, left, mid);
} else {
add(line, (node << 1) + 1, mid, right);
}
}
};
} // namespace emthrm
#endif // EMTHRM_DYNAMIC_PROGRAMMING_LI_CHAO_TREE_HPP_
#line 1 "include/emthrm/dynamic_programming/li_chao_tree.hpp"
#include <algorithm>
#include <bit>
#include <cassert>
#include <iterator>
#include <numeric>
#include <utility>
#include <vector>
namespace emthrm {
template <typename T, bool IS_MINIMIZED = true>
struct LiChaoTree {
struct Line {
T a, b;
explicit Line(const T a, const T b) : a(a), b(b) {}
T f(const T x) const { return a * x + b; }
};
explicit LiChaoTree(const std::vector<T>& xs_, const T inf) : n(1), xs(xs_) {
std::sort(xs.begin(), xs.end());
xs.erase(std::unique(xs.begin(), xs.end()), xs.end());
assert(xs.size() > 0);
n = std::bit_ceil(xs.size());
const T xs_back = xs.back();
xs.resize(n, xs_back);
dat.assign(n << 1, Line(0, inf));
}
void add(T a, T b) {
if constexpr (!IS_MINIMIZED) {
a = -a;
b = -b;
}
Line line(a, b);
add(&line, 1, 0, n);
}
void add(T a, T b, T left, T right) {
if constexpr (!IS_MINIMIZED) {
a = -a;
b = -b;
}
for (int len = 1,
node_l = std::distance(
xs.begin(), std::lower_bound(xs.begin(), xs.end(), left)),
node_r = std::distance(
xs.begin(), std::lower_bound(xs.begin(), xs.end(), right)),
l = node_l + n, r = node_r + n;
l < r;
l >>= 1, r >>= 1, len <<= 1) {
if (l & 1) {
Line line(a, b);
add(&line, l++, node_l, node_l + len);
node_l += len;
}
if (r & 1) {
Line line(a, b);
node_r -= len;
add(&line, --r, node_r, node_r + len);
}
}
}
T query(const T x) const {
int node = n + std::distance(xs.begin(),
std::lower_bound(xs.begin(), xs.end(), x));
T res = dat[node].f(x);
while (node >>= 1) {
if (dat[node].f(x) < res) res = dat[node].f(x);
}
return IS_MINIMIZED ? res : -res;
}
private:
int n;
std::vector<T> xs;
std::vector<Line> dat;
void add(Line* line, int node, int left, int right) {
const bool flag_l = dat[node].f(xs[left]) <= line->f(xs[left]);
const bool flag_r = dat[node].f(xs[right - 1]) <= line->f(xs[right - 1]);
if (flag_l && flag_r) return;
if (!flag_l && !flag_r) {
std::swap(dat[node], *line);
return;
}
const int mid = std::midpoint(left, right);
if (line->f(xs[mid]) < dat[node].f(xs[mid])) std::swap(dat[node], *line);
if (line->f(xs[left]) <= dat[node].f(xs[left])) {
add(line, node << 1, left, mid);
} else {
add(line, (node << 1) + 1, mid, right);
}
}
};
} // namespace emthrm
Back to top page