Li Chao Tree (CHT)

O(log C) per line / query

Maintain a set of lines y = kx + m and answer 'minimum y at x' queries: the convex hull trick without any monotonicity requirements on slopes or queries. Each tree node keeps the line that wins at its segment's midpoint; inserting recurses into the half where the loser might still win. This is what turns dp[i] = min over j of (dp[j] + k[j]·x[i] + m[j]) from O(n²) into O(n log C).

// use it on

Codeforces 319C: Kalila and Dimna in the Logging Industry

dp[i] = min over j of (dp[j] + b[j]·a[i]): insert line (b[j], dp[j]) after computing each dp[j], query at a[i]. The canonical CHT-as-DP-speedup problem.

// the code

typedef long long ll;
struct LiChao {  // minimum at integer x in [lo, hi); sparse, big domains OK
    ll lo, hi;
    unordered_map<ll, pair<ll, ll>> line;  // heap node -> line (k, m)
    LiChao(ll lo, ll hi) : lo(lo), hi(hi) {}
    ll val(pair<ll, ll> f, ll x) { return f.first * x + f.second; }
    void add(ll k, ll m) {
        pair<ll, ll> nw = {k, m};
        ll v = 1, l = lo, r = hi;
        while (true) {
            if (!line.count(v)) { line[v] = nw; return; }
            ll mid = (l + r) / 2;
            if (val(nw, mid) < val(line[v], mid)) swap(nw, line[v]);
            if (r - l == 1) return;
            if (val(nw, l) < val(line[v], l)) { v = 2 * v; r = mid; }
            else if (val(nw, r - 1) < val(line[v], r - 1)) { v = 2 * v + 1; l = mid; }
            else return;  // loser is beaten everywhere here
        }
    }
    ll query(ll x) {
        ll v = 1, l = lo, r = hi, res = LLONG_MAX;
        while (true) {
            if (line.count(v)) res = min(res, val(line[v], x));
            if (r - l == 1) return res;
            ll mid = (l + r) / 2;
            if (x < mid) { v = 2 * v; r = mid; }
            else { v = 2 * v + 1; l = mid; }
        }
    }
};
// careful: k*x + m must fit in long long at the domain edges

// more digits & optimization