yohandi
A recent graduate with a strong interest in algorithms and data structures.

[Tutorial] Li Chao Tree

Li Chao Tree is a data structure that offers a method to insert specific functions and query the minimum (or maximum) value at a point in logarithmic time. It was first introduced by Li Chao during a lecture in 2013.

For any pair of inserted functions,

KaTeX can only parse string typed expression
and
KaTeX can only parse string typed expression
(where
KaTeX can only parse string typed expression
), there exists a point
KaTeX can only parse string typed expression
such that:

  • KaTeX can only parse string typed expression
    when
    KaTeX can only parse string typed expression
    , and
  • KaTeX can only parse string typed expression
    when
    KaTeX can only parse string typed expression

(or the conditions could be in reverse order) for all

KaTeX can only parse string typed expression
in the possible query range. The concept behind the Li Chao Tree bears some resemblance to the Segment Tree, but what distinguishes it is the unique way it stores function information in each node.

Motivational Problem

You are given

KaTeX can only parse string typed expression
functions
KaTeX can only parse string typed expression
(where
KaTeX can only parse string typed expression
). For each of the
KaTeX can only parse string typed expression
queries, determine
KaTeX can only parse string typed expression
for a given
KaTeX can only parse string typed expression
.

Constraints given:

  • KaTeX can only parse string typed expression
  • KaTeX can only parse string typed expression
    (for each
    KaTeX can only parse string typed expression
    such that
    KaTeX can only parse string typed expression
    )
  • KaTeX can only parse string typed expression
    (for each
    KaTeX can only parse string typed expression
    such that
    KaTeX can only parse string typed expression
    )
  • KaTeX can only parse string typed expression
    (for each
    KaTeX can only parse string typed expression
    such that
    KaTeX can only parse string typed expression
    )

This problem comes straight from CodeChef - Polynomials.

Li Chao Tree

Much like the Segment Tree, we want to perform a traversal of nodes from root to leaf. Each node is expected to store a function information such that when we traverse from root to leaf, it is guaranteed that one of the functions met in the path gives the minimum (or maximum) value. For the sake of simplicity, let's assume that our problem queries for minimum value.

Suppose we want to insert a function

KaTeX can only parse string typed expression
. Denote
KaTeX can only parse string typed expression
as a function that were saved in the current node resulting from previous insertions. Define
KaTeX can only parse string typed expression
as the middle-point of the range, e.g. a segment
KaTeX can only parse string typed expression
, in the current node. Then, we may perform two comparisons:
KaTeX can only parse string typed expression
with
KaTeX can only parse string typed expression
and
KaTeX can only parse string typed expression
with
KaTeX can only parse string typed expression
.

  • Case

    KaTeX can only parse string typed expression
    and
    KaTeX can only parse string typed expression
    :

    This implies that there are three possibilities: there is no

    KaTeX can only parse string typed expression
    or
    KaTeX can only parse string typed expression
    or
    KaTeX can only parse string typed expression
    .

    • there is no
      KaTeX can only parse string typed expression
      that meets our criteria; hence, no action is required.
    • KaTeX can only parse string typed expression
      , and again, no action is needed as it is outside our considered interval.
    • KaTeX can only parse string typed expression
      , which means
      KaTeX can only parse string typed expression
      may affect the second half of the current segment, i.e.
      KaTeX can only parse string typed expression
      . Thus, we need to recurse to the right child of the current node to handle that case.

    We also don't need to update the current node as

    KaTeX can only parse string typed expression
    when
    KaTeX can only parse string typed expression
    . Whenever a query of
    KaTeX can only parse string typed expression
    is performed, the traversal from root to leaf is guaranteed to pass through this node.

  • Case

    KaTeX can only parse string typed expression
    and
    KaTeX can only parse string typed expression
    :

    This mirrors the first case with identical potential scenarios. However, since root-to-leaf traversal will definitely traverse this node for any query in the range

    KaTeX can only parse string typed expression
    , the current node should be updated to
    KaTeX can only parse string typed expression
    to ensure the minimum value can be obtained.

  • Case

    KaTeX can only parse string typed expression
    and
    KaTeX can only parse string typed expression
    :

    This implies that

    KaTeX can only parse string typed expression
    is located somewhere in
    KaTeX can only parse string typed expression
    , and we need to recurse to the left child of the current node to handle that case. We don't need to update the current node as
    KaTeX can only parse string typed expression
    when
    KaTeX can only parse string typed expression
    . Whenever a query of
    KaTeX can only parse string typed expression
    is performed, the traversal from root to leaf is guaranteed to pass through this node.

  • Case

    KaTeX can only parse string typed expression
    and
    KaTeX can only parse string typed expression
    :

    This implies that

    KaTeX can only parse string typed expression
    is located somewhere in
    KaTeX can only parse string typed expression
    , and we need to recurse to the left child of the current node to handle that case. As the traversal from root to leaf is guaranteed to pass through this node whenever a query of
    KaTeX can only parse string typed expression
    is performed, we need to update the current node
    KaTeX can only parse string typed expression
    so that we can retrieve the minimum value.

Below, the implementation for inserting the function is showcased. Bear in mind that the struct Node in this code is tailored to the problem described. Adjustments may be necessary based on specific needs.

using ll = long long;

struct Function {
  ll a, b, c, d;
  Function(ll a = 0, ll b = 0, ll c = 0, ll d = 0) : a(a), b(b), c(c), d(d) {}
  ll f(ll x) const { return a * x * x * x + b * x * x + c * x + d; }
};

const ll INF = 1e18;

struct Node {
  Function function{0, 0, 0, INF};
  Node *left = nullptr, *right = nullptr;
};

Node *root;
const int S = 350, L = S + 1, R = 1e5 + 1;

void insertFunction(Function newFunction, Node *node = root, int l = L,
                    int r = R) {
  if (!node)
    return;
  int m = (l + r) >> 1;
  bool left = newFunction.f(l) < node->function.f(l);
  bool mid = newFunction.f(m) < node->function.f(m);
  if (mid)
    swap(newFunction, node->function);
  if (l == r - 1)
    return;
  Node *&child = left != mid ? node->left : node->right;
  if (!child)
    child = new Node();
  insertFunction(newFunction, child, left != mid ? l : m, left != mid ? m : r);
}

Notice that the implementation is similar to Dynamic Segment Tree as it is using pointer and it allows a quite wide range interval of query. It is possible to deploy Li Chao Tree using the common array-based to store the node value. However, this comes at the expense of being restricted to a narrower index range.

Then, to determine which child node to traverse to, we simply follow the node that contains the range of the point of query. The implementation is as follows:

ll query(int x, Node *node = root, int l = L, int r = R) {
  if (!node || l > x || r <= x)
    return INF;
  int m = (l + r) >> 1;
  ll res = node->function.f(x);
  return x < m ? min(res, query(x, node->left, l, m))
               : min(res, query(x, node->right, m, r));
}

Solution

If we try to naively apply the polynomial function, we won't be able to meet the criteria of Li Chao Tree; consequently, this definitely results in Wrong Answer verdict. As melfice stated in his tutorial post, "a polynomial

KaTeX can only parse string typed expression
has at most one root greater than
KaTeX can only parse string typed expression
". Proof can be found here.

This implies that if we only consider

KaTeX can only parse string typed expression
(instead of
KaTeX can only parse string typed expression
) as our main range, then, we can effectively integrate the polynomial function into the Li Chao Tree. Then, for range
KaTeX can only parse string typed expression
, we can simply update it by iterating it over as
KaTeX can only parse string typed expression
.

int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);

  int T;
  cin >> T;
  while (T--) {
    int N, Q;
    cin >> N;
    vector<Function> functions(N);
    for (auto &f : functions)
      cin >> f.d >> f.c >> f.b >> f.a;
    cin >> Q;
    vector<int> t(Q);
    for (auto &x : t)
      cin >> x;

    root = new Node();
    vector<ll> ans(S + 1, INF);

    for (const auto &func : functions) {
      for (int x = 0; x <= S; ++x)
        ans[x] = min(ans[x], func.f(x));
      insertFunction(func);
    }

    for (const auto &x : t)
      cout << (x <= S ? ans[x] : query(x)) << "\n";
  }
  return 0;
}

Denote

KaTeX can only parse string typed expression
as
KaTeX can only parse string typed expression
. Then, the overall time complexity for the implementation is:

KaTeX can only parse string typed expression

My full solution is accessible here.

Li Chao Tree on Convex Hull Trick

While it is potent, the traditional Convex Hull Trick comes with a limitation: it requires the gradients to be strictly monotonous. One might adopt a dynamic approach by using

KaTeX can only parse string typed expression
instead of
KaTeX can only parse string typed expression
; however, this alternative introduces complexities, making it far from straightforward. Li Chao Tree is able to address those problems. If you are not familiar with the Convex Hull Trick yet, I suggest you to read my previous blog first. You can find it here.

In Convex Hull Trick, we may notice that for all pairs of functions, the number of intersection point is at most once. For affine functions

KaTeX can only parse string typed expression
and
KaTeX can only parse string typed expression
(
KaTeX can only parse string typed expression
), the x-coordinate of the intersection point is located at
KaTeX can only parse string typed expression
. We may assign
KaTeX can only parse string typed expression
with it and have the required condition of Li Chao Tree satisfied. Thus, the Convex Hull Trick can be implemented using the Li Chao Tree (although with a potential of having extra memory space). If you're curious about the practical application of this concept, you can check out my implementation for the problem discussed in the Convex Hull Trick post here.

References

  1. melfice, "POLY - Editorial," CodeChef Discussion. https://discuss.codechef.com/t/poly-editorial/17335.

  2. rama_pang, "[Tutorial] Li Chao Tree Extended," Codeforces. https://codeforces.com/blog/entry/86731.

© 2023 Yohandi. All rights reserved.