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
fi(x) and
KaTeX can only parse string typed expression
fj(x) (where
KaTeX can only parse string typed expression
1≤i<j≤n), there exists a point
KaTeX can only parse string typed expression
p such that:
KaTeX can only parse string typed expression
fi(x)≤fj(x) when
KaTeX can only parse string typed expression
x≤p, and
KaTeX can only parse string typed expression
fi(x)≥fj(x) when
KaTeX can only parse string typed expression
x≥p
(or the conditions could be in reverse order) for all
KaTeX can only parse string typed expression
x 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.
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
fj(x). Denote
KaTeX can only parse string typed expression
g(x) as a function that were saved in the current node resulting from previous insertions. Define
KaTeX can only parse string typed expression
mid as the middle-point of the range, e.g. a segment
KaTeX can only parse string typed expression
[L,R), in the current node. Then, we may perform two comparisons:
KaTeX can only parse string typed expression
fj(L) with
KaTeX can only parse string typed expression
g(L) and
KaTeX can only parse string typed expression
fj(mid) with
KaTeX can only parse string typed expression
g(mid).
Case
KaTeX can only parse string typed expression
fj(L)≥g(L) and
KaTeX can only parse string typed expression
fj(mid)≥g(mid):
This implies that there are three possibilities: there is no
KaTeX can only parse string typed expression
p or
KaTeX can only parse string typed expression
p<L or
KaTeX can only parse string typed expression
p≥mid.
there is no
KaTeX can only parse string typed expression
p that meets our criteria; hence, no action is required.
KaTeX can only parse string typed expression
p<L, and again, no action is needed as it is outside our considered interval.
KaTeX can only parse string typed expression
p≥mid, which means
KaTeX can only parse string typed expression
p may affect the second half of the current segment, i.e.
KaTeX can only parse string typed expression
[mid,R). 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
g(x)≤fj(x) when
KaTeX can only parse string typed expression
L≤x≤mid. Whenever a query of
KaTeX can only parse string typed expression
L≤x≤mid is performed, the traversal from root to leaf is guaranteed to pass through this node.
Case
KaTeX can only parse string typed expression
fj(L)<g(L) and
KaTeX can only parse string typed expression
fj(mid)<g(mid):
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
[L,mid), the current node should be updated to
KaTeX can only parse string typed expression
g(x):=fj(x) to ensure the minimum value can be obtained.
Case
KaTeX can only parse string typed expression
fj(L)<g(L) and
KaTeX can only parse string typed expression
fj(mid)≥g(mid):
This implies that
KaTeX can only parse string typed expression
p is located somewhere in
KaTeX can only parse string typed expression
[L,mid), 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
g(x)≤fj(x) when
KaTeX can only parse string typed expression
x≥mid. Whenever a query of
KaTeX can only parse string typed expression
mid≤x<R is performed, the traversal from root to leaf is guaranteed to pass through this node.
Case
KaTeX can only parse string typed expression
fj(L)≥g(L) and
KaTeX can only parse string typed expression
fj(mid)<g(mid):
This implies that
KaTeX can only parse string typed expression
p is located somewhere in
KaTeX can only parse string typed expression
[L,mid), 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
mid≤x<R is performed, we need to update the current node
KaTeX can only parse string typed expression
g(x):=fj(x) 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 =longlong;structFunction{ 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;structNode{ Function function{0,0,0, INF}; Node *left = nullptr,*right = nullptr;};Node *root;constint S =350, L = S +1, R =1e5+1;voidinsertFunction(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