struct Line { ll a, b; ll eval(ll x) const { return a*x + b; } }; class LiChao { private: const static ll INF = 4e18; vector<Line> tree; // Tree of lines vector<ll> xs; // x-coordinate of point i int k = 1; // Log-depth of the tree int mapInd(int j) const { int z = __builtin_ctz(j); return ((1<<(k-z)) | (j>>z)) >> 1; } bool comp(const Line& a, int i, int j) const { return a.eval(xs[j]) < tree[i].eval(xs[j]); } public: LiChao(const vector<ll>& points) { while(points.size() >> k) ++k; tree.resize(1 << k, {0, INF}); xs.resize(1 << k, points.back()); for (int i = 0; i < points.size(); ++i) xs[mapInd(i+1)] = points[i]; } void addLine(Line line) { for (int i = 1; i < tree.size();) { if (comp(line, i, i)) swap(line, tree[i]); if (line.a > tree[i].a) i = 2*i; else i = 2*i+1; } } ll minVal(int j) const { j = mapInd(j+1); ll res = INF; for (int i = j; i > 0; i /= 2) res = min(res, tree[i].eval(xs[j])); return res; } };
Tässä mapInd-funktio kuvaa indeksit sijainteihin puussa. Jos puun koko on 7, ja arvot x ovat 1, 2, \dots, 7, puun indeksointi on seuraava
ja arvot x sen solmuissa ovat seuraavat
Toinen, segmenttipuutyylinen toteutus:
const int N = 1<<20; const ll INF = 4e18; struct Line { // Nämä voi usein tallentaa intteinä, mutta tällöin // "ääretön" suora pitää toteuttaa varovasti. ll k, c=INF; ll operator()(ll x) { return k*x + c; } } lct[2*N]; Line lc_get(int p, int cn=1, int x=0, int y=N-1) { if (x == y) return lct[cn]; int m = (x+y)/2; Line got; if (p <= m) got = lc_get(p, 2*cn, x, m); else got = lc_get(p, 2*cn+1, m+1, y); if (lct[cn](p) < got(p)) return lct[cn]; else return got; } void lc_add(Line nw, int cn=1, int x=0, int y=N-1) { Line &old = lct[cn]; int m = (x+y)/2; bool better_x = nw(x) < old(x); bool better_m = nw(m) < old(m); if (better_m) swap(old, nw); if (x != y) { if (better_x != better_m) { lc_add(nw, 2*cn, x, m); } else { lc_add(nw, 2*cn+1, m+1, y); } } }