概要
動的セグメント木上でLi-Chao (Segment) Treeを処理する。
動的セグメント木上で処理することで、座標先読みなしで大きい区間 \([0, N]\) に対するConvex Hull Trickの問題を解くことができる。
計算量
扱う区間を \([0, N]\) とした時、
-
直線追加: \(O(\log N)\)
-
線分追加: \(O(\log^2 N)\)
-
最小値計算: \(O(\log N)\)
実装
#include<algorithm>
using ll = long long;
using namespace std;
class LiChaoTree {
struct Line {
ll a, b;
ll f(ll x) const { return a*x + b; }
Line(ll a, ll b) : a(a), b(b) {}
};
struct Node {
Node *left, *right;
Line line;
Node(Line line) : left(nullptr), right(nullptr), line(line) {}
};
const ll inf = 1e16L;
const Line inf_line = Line{0, inf};
Node* root;
ll xl, xr;
Node* _add_line(Node *nd, Line line, ll l, ll r) {
if(l == r) return nullptr;
ll m = (l + r) >> 1;
if(nd == nullptr) return new Node(line);
bool left = (line.f(l) <= nd->line.f(l));
bool mid = (line.f(m) <= nd->line.f(m));
bool right = (line.f(r) <= nd->line.f(r));
if(left && right) {
nd->line = line;
return nd;
}
if(!left && !right) {
return nd;
}
if(mid) {
swap(nd->line, line);
}
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_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);
}
if(nd == nullptr) {
nd = new Node(inf_line);
}
ll m = (l + r) >> 1;
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(ll k, ll l, ll r) {
Node *nd = root;
ll s = inf;
while(r-l > 0 && nd != nullptr) {
ll m = (l + r) >> 1;
s = min(s, nd->line.f(k));
if(k < m) {
r = m;
nd = nd->left;
} else {
l = m;
nd = nd->right;
}
}
return s;
}
public:
// [xl, xr)
LiChaoTree(ll xl, ll xr) : xl(xl), xr(xr), root(nullptr) {}
void add_line(ll a, ll b) {
Line line = Line{a, b};
root = _add_line(root, line, xl, xr);
}
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);
}
ll query(ll x) {
return _query(x, xl, xr);
}
};
Verified
-
AtCoder: "早稲田大学プログラミングコンテスト - I問題: Ramen": source (C++14, 1128ms)