WQS Binary Search

Introduction

This article introduces WQS binary search, a method for optimizing dynamic programming problems. It is also called weighted binary search, convex optimization DP, convex total monotonicity DP, or the Lagrange multiplier method in different articles, and is known internationally as the Aliens Trick. It was first summarized by Wang Qinshi in “An Analysis of a Class of Binary Search Methods”.

WQS binary search is usually used for optimization problems with a cardinality constraint where direct solving is expensive, but which become much easier once the constraint is removed.

For example, suppose we must select mm items from nn items while optimizing a complicated objective. If the optimal objective value after selecting jj items from the first ii items is f(i,j)f(i,j), the answer is f(n,m)f(n,m). The transition is usually two-dimensional. Directly implementing it takes O(nm)O(nm) time, which is unacceptable.

Now suppose that the unconstrained optimization problem is easy. The optimal number selected may not satisfy the original cardinality constraint. If too many items are selected, attach a fixed penalty kk to every selected item (the “weight” in weighted binary search) and solve the unconstrained problem again. Different values of kk produce different optimal cardinalities, and the optimal cardinality changes monotonically with kk. Thus binary search can find the kk for which the optimal cardinality is exactly mm. If the optimal value of the penalized problem is fk(n)f_k(n), removing the value lost to the added penalties gives the original answer f(n,m)=fk(n)+kmf(n,m)=f_k(n)+km. If one penalized problem takes O(T(n))O(T(n)) time, the total complexity becomes O(T(n)log⁡L)O(T(n)\log L), where O(log⁡L)O(\log L) is the number of binary-search iterations for kk.

This is the basic idea of WQS binary search. It works only when f(n,m)f(n,m) is convex in mm; otherwise, there may be no penalty kk whose optimal cardinality is exactly mm. This is why this DP optimization is often called convex optimization DP or convex total monotonicity DP.

The Traditional Method

Let XX be a nonempty (finite) decision space, let f:X→Rf:X\rightarrow\mathbf R be the objective function, and let g:X→Rdg:X\rightarrow\mathbf R^d be a function imposing constraints. The problem can be viewed as evaluating the value function v(y)v(y) of the following optimization problem:

v(y)=min⁡x∈X  f(x)subject to g(x)=y.\begin{aligned} v(y)=\min_{x\in X}\;&f(x)\\ \text{subject to }&g(x)=y. \end{aligned}

For the cardinality-constrained example above, XX can be viewed as the family of subsets of all items, x∈Xx\in X as one subset, f(x)f(x) as its value, and g(x)g(x) as the number of elements in subset xx. Of course, g(x)g(x) need not represent cardinality; broader constraints appear later.

Convention

For convenience, this article discusses only minimization objectives. Maximization objectives are analogous, except that convex functions below should be replaced by concave functions (also called upper-convex functions). Alternatively, adding a negative sign converts a maximization problem into minimization of its negation.

Geometric Intuition

Most problems in algorithm contests are combinatorial optimization problems, so the decision space XX usually has no useful structure. We can instead consider

D={(g(x),f(x))∈R×Rd:x∈X}.\mathcal D = \{(g(x),f(x))\in\mathbf R\times\mathbf R^d:x\in X\}.

The traditional method mainly handles d=1d=1, when there is only one constraint. The figure below shows one possible point set D\mathcal D in this case.

Figure: wqs f g space

The red and blue points are the projections of all possible choices in XX onto the plane (g(x),f(x))(g(x),f(x)), forming D\mathcal D. The original problem asks for the smallest vertical coordinate among the points whose horizontal coordinate is yy, namely v(y)v(y). As yy changes, all points (y,v(y))(y,v(y)) form the set of red points in the figure.

To obtain the vertical coordinate of (y,v(y))(y,v(y)), consider a line of slope λ∈R\lambda\in\mathbf R tangent to D\mathcal D. When the slope is appropriate, the line through (y,v(y))(y,v(y)) has the smallest intercept f(x)−λg(x)f(x)-\lambda g(x) among all lines of slope λ\lambda passing through points of D\mathcal D. Let this minimum be

h(λ)=min⁡x∈Xf(x)−λg(x).h(\lambda) = \min_{x\in X}f(x)-\lambda g(x).

Since (y,v(y))(y,v(y)) also lies on this line, the solution of the original problem is

v(y)=h(λ)+λy.v(y) = h(\lambda) + \lambda y.

Suppose h(λ)h(\lambda) is easy to compute for every reasonable λ\lambda. This is often true in contests because the original constraint has been removed. The two most important questions are then:

  1. Does there exist a slope λ\lambda whose minimum-intercept line passes through (y,v(y))(y,v(y))?
  2. If so, how can we find such a slope λ\lambda?

The first question is relatively easy. As the slope λ\lambda changes, the set cut out by all these lines (the intersection of their corresponding upper half-planes) is necessarily convex. Therefore, these lines can pass through a point if and only if the point lies on the lower convex hull of this convex set. Equivalently, v(y)v(y) is a convex function.

The second question is subtler. Since the desired point is known to have horizontal coordinate yy, a natural idea is to compute g(x)g(x) at the current optimal solution xλx_\lambda while computing h(λ)h(\lambda). In the preceding example, when solving the penalized problem, record the number of selected items in an optimal solution. Compare g(xλ)g(x_\lambda) with the desired yy, and adjust the next λ\lambda interval accordingly. This is the most traditional WQS binary-search method.

In summary, the traditional WQS binary-search process is:

  1. Initially, choose a reasonable interval for λ\lambda.
  2. Choose a λ\lambda in the current interval.
  3. Solve h(λ)=min⁡x∈Xf(x)−λg(x)h(\lambda)=\min_{x\in X}f(x)-\lambda g(x) and record g(x)g(x) at an optimal solution xλx_\lambda, obtaining g(xλ)g(x_\lambda).
  4. If g(xλ)=yg(x_\lambda)=y, the optimal value is v(y)=h(λ)+λyv(y)=h(\lambda)+\lambda y; terminate.
  5. Otherwise, adjust the interval for λ\lambda according to the relation between g(xλ)g(x_\lambda) and yy, then return to step 2.

This basic process is enough for some problems, but it is not complete. The improvements to it are discussed next.

Handling Collinear Cases

The first problem with the basic process is that it cannot handle collinear points correctly.

If three or more red points on the lower convex hull of D\mathcal D are collinear, the basic process may fail to compare g(xλ)g(x_\lambda) and yy correctly. Suppose three collinear red points have horizontal coordinates y1,y2,y3y_1,y_2,y_3 and their common line has slope λ∗\lambda^*. To compute v(y2)v(y_2) correctly, the last problem computed when the algorithm terminates must be h(λ∗)h(\lambda^*), because λ∗\lambda^* is the only slope of a minimum-intercept line that can pass through (y2,v(y2))(y_2,v(y_2)). However, while computing h(λ∗)h(\lambda^*), the recorded value g(xλ∗)g(x_{\lambda^*}) may be any of y1,y2,y3y_1,y_2,y_3. If the recorded g(xλ∗)g(x_{\lambda^*}) is not y2y_2, the algorithm continues incorrectly and adjusts the λ\lambda interval away from y2y_2, eventually producing a wrong answer.

One way to handle collinearity is always to make g(xλ)g(x_\lambda) as large as possible (or as small as possible) when recording the optimal solution xλx_\lambda. At the same time, change the binary-search termination condition from finding g(xλ)=yg(x_\lambda)=y for some λ\lambda to finding the smallest (or largest) λ\lambda satisfying g(xλ)≥yg(x_\lambda)\ge y (or g(xλ)≤yg(x_\lambda)\le y). In the example above, this means that computing h(λ∗)h(\lambda^*) returns g(xλ∗)g(x_{\lambda^*}) equal to y3y_3, ensuring that the last computed problem is h(λ∗)h(\lambda^*). Note that the final output is h(λ)+λyh(\lambda)+\lambda y, not h(λ)+λg(xλ)h(\lambda)+\lambda g(x_{\lambda}), because the recorded g(xλ)g(x_\lambda) need not equal the actual constraint yy.

Another approach is binary search over real numbers. If all numbers in the problem are integers, the slope in WQS binary search is clearly also an integer. Real-valued binary search is introduced so that if the correct choice λ∗\lambda^* is accidentally discarded, the fractional part can move the search back toward it, ultimately approaching the correct answer λ∗\lambda^*. For example, if the recorded g(xλ∗)g(x_{\lambda^*}) is y1y_1, smaller than the desired y2y_2, while computing h(λ∗)h(\lambda^*), the algorithm considers (λ∗,λr](\lambda^*,\lambda_r], where λr\lambda_r is the right endpoint of the current λ\lambda interval. For integers, this should actually be [λ∗+1,λr][\lambda_*+1,\lambda_r], which rules out approaching λ∗\lambda^* later. With real binary search, the interval remains (λ∗,λr](\lambda^*,\lambda_r]; for λ\lambda in this interval, computing h(λ)h(\lambda) always records a value g(xλ)g(x_\lambda) at least y3y_3, strictly greater than y2y_2. The right half is therefore repeatedly discarded as the algorithm proceeds, and the final range of λ\lambda is guaranteed to lie near λ∗\lambda^*. Since the desired slope is known to be an integer, the final precision need only ensure that the binary-search interval contains one integer; that integer is the desired λ∗\lambda^*.

After collinear cases are handled correctly, WQS binary search solves most WQS binary-search problems encountered in algorithm contests. However, it still cannot handle cases where g(xλ)g(x_\lambda) is difficult to record, nor higher-dimensional cases with multiple coplanar points. We now examine the properties of the optimization problem v(y)v(y) and give a more general method.

The Dual Method

This section presents a WQS binary-search implementation that only requires, for every λ∈Rd\lambda\in\mathbf R^d, efficient computation of

h(λ)=min⁡x∈Xf(x)−λ⋅g(x)h(\lambda) = \min_{x\in X}f(x)-\lambda\cdot g(x)

the value, together with convexity of the original value function v(y)v(y) over y∈Rdy\in\mathbf R^d1. In one sentence, we prove that the value function v(y)v(y) of the original problem equals the optimal value of its dual problem,

v⋆(y)=sup⁡λ∈Rdh(λ)+λ⋅y,v^\star(y) = \sup_{\lambda\in\mathbf R^d} h(\lambda)+\lambda\cdot y,

and that the dual objective is concave in λ∈Rd\lambda\in\mathbf R^d, hence unimodal. It can therefore be solved efficiently by ternary search or golden-section search, with complexity still O(T(n)log⁡dL)O(T(n)\log^d L). This completely resolves the problem of recording g(xλ)g(x_\lambda) in the traditional method and allows WQS binary search to be applied in higher dimensions.

This section also shows that the range of g(xλ)g(x_\lambda) can be obtained from h(λ)h(\lambda) without recording it separately while solving h(λ)h(\lambda). For example, when d=1d=1 and the problem involves only integers, the range of g(xλ)g(x_\lambda) is exactly

[h(λ−1)−h(λ),h(λ)−h(λ+1)].[h(\lambda-1)-h(\lambda),h(\lambda)-h(\lambda+1)].

This provides another way to handle collinear cases in problems that must use the binary-search process described above.

The conclusions are proved using convex analysis. Specific applications can be found in the Example Problems section.

Lagrange Duality

Consider solving the problem with the Lagrange multiplier method. Introduce a Lagrange multiplier λ∈Rd\lambda\in\mathbf R^d. The Lagrangian is

L(x,λ,y)=f(x)−λ⋅g(x)+λ⋅y.L(x,\lambda,y) = f(x) - \lambda\cdot g(x)+\lambda\cdot y.

If any component of g(x)−yg(x)-y is nonzero, the corresponding component of λ\lambda can tend to positive or negative infinity, so

sup⁡λ∈RdL(x,λ,y)={f(x),g(x)=y,+∞,otherwise.\sup_{\lambda\in\mathbf R^d}L(x,\lambda,y) = \begin{cases} f(x),&g(x)=y,\\ +\infty,&\text{otherwise}. \end{cases}

Thus the original problem can be written as

v(y)=min⁡x∈Xsup⁡λ∈RdL(x,λ,y).\begin{aligned} v(y) &= \min_{x\in X}\sup_{\lambda\in\mathbf R^d}L(x,\lambda,y). \end{aligned}

Exchanging the two optimization operations gives the dual problem:

v⋆(y)=sup⁡λ∈Rdmin⁡x∈XL(x,λ,y)=sup⁡λ∈Rdh(λ)+λ⋅y.\begin{aligned} v^\star(y)&=\sup_{\lambda\in\mathbf R^d}\min_{x\in X}L(x,\lambda,y)\\ &=\sup_{\lambda\in\mathbf R^d}h(\lambda)+\lambda\cdot y. \end{aligned}

We will show that under the condition that v(y)v(y) is convex in yy, strong duality holds: v⋆(y)=v(y)v^\star(y)=v(y).

Convex Conjugate

To establish strong duality, we need the notion of a convex conjugate.

Convex Conjugate

For a function f:Rd→R∪{±∞}f:\mathbf R^d\rightarrow\mathbf R\cup\{\pm\infty\}, its convex conjugate, also called the Legendre–Fenchel transformation, is

f∗(x∗)=sup⁡x∈Rdx∗⋅x−f(x).f^*(x^*) = \sup_{x\in\mathbf R^d}x^*\cdot x - f(x).

Viewed as a function of x∗x^*, f∗(x∗)f^*(x^*) is the supremum of a family of linear functions, so it is necessarily convex on Rd\mathbf R^d.

“Slope Vector” and “Intercept” of a Hyperplane

Every hyperplane in the vector space Rd+1\mathbf R^{d+1} discussed here has the form

y=k⋅x+b.y = k\cdot x + b.

Thus, hyperplanes parallel to the yy-axis are not considered. For convenience, we informally call kk the “slope vector” and bb the “intercept” of the hyperplane. Writing the equation in standard form gives

k⋅x−y=−b.k\cdot x - y = -b.

A normal vector is (k,−1)(k,-1). Thus, the so-called slope vector consists of the first dd components of the normal vector after normalizing its last component to −1-1.

Geometrically, the convex conjugate of f(x)f(x) describes hyperplanes with slope vector x∗x^* that intersect the epigraph of the function f(x)f(x)

epi⁡f={(x,y)∈Rd×R:f(x)≤y}\operatorname{epi}f = \{(x,y)\in\mathbf R^d\times\mathbf R:f(x)\le y\}

of f(x)f(x). The minimum intercept f(x)−x∗⋅xf(x)-x^*\cdot x among these hyperplanes is −f∗(x∗)-f^*(x^*). In other words, f(x)f(x) always lies above the hyperplane y=x∗⋅x−f∗(x∗)y = x^*\cdot x-f^*(x^*), and the hyperplane is tangent at (x0,f(x0))(x_0,f(x_0)), possibly with other tangent points. Such a hyperplane is a supporting hyperplane of f(x)f(x) at x0x_0. The intercept of a supporting hyperplane is uniquely determined by its slope vector, and the convex conjugate provides this mapping from slope vector to intercept.

Minimizing f(x)−λ⋅g(x)f(x)-\lambda\cdot g(x) over XX is equivalent to minimizing v(y)−λ⋅yv(y)-\lambda\cdot y over {(y,v(y))}\{(y,v(y))\}:

min⁡xf(x)−λ⋅g(x)=min⁡y∈g(X)(min⁡x∈X:g(x)=yf(x)−λ⋅g(x))=min⁡y∈g(X)(min⁡x∈X:g(x)=yf(x))−λ⋅y=min⁡y∈g(X)v(y)−λ⋅y.\begin{aligned} \min_x f(x)-\lambda\cdot g(x) &= \min_{y\in g(X)}\left(\min_{x\in X:g(x)=y} f(x) - \lambda\cdot g(x)\right)\\ &= \min_{y\in g(X)}\left(\min_{x\in X:g(x)=y} f(x)\right) - \lambda\cdot y \\ &= \min_{y\in g(X)}v(y) - \lambda\cdot y. \end{aligned}

Therefore,

h(λ)=min⁡y∈g(X)v(y)−λ⋅y=−v∗(λ).h(\lambda) = \min_{y\in g(X)}v(y) - \lambda\cdot y = -v^*(\lambda).

Thus h(λ)h(\lambda) is concave in λ∈Rd\lambda\in\mathbf R^d, and

v⋆(y)=sup⁡λ∈Rdλ⋅y−v∗(λ)=v∗∗(y).v^\star(y) = \sup_{\lambda\in\mathbf R^d}\lambda\cdot y-v^*(\lambda) = v^{**}(y).

The value function v⋆(y)v^{\star}(y) of the dual problem is therefore the double convex conjugate of the original value function v(y)v(y), also called the biconjugate.

The problem becomes: which functions v(y)v(y) equal their own biconjugate? The following theorem answers this.

Theorem (Fenchel–Moreau)

For f:Rd→R∪{±∞}f:\mathbf R^d\rightarrow\mathbf R\cup\{\pm\infty\}, f∗∗=ff^{**}=f if and only if one of the following holds:

  1. f(x)f(x) is a proper convex function and lower semicontinuous;
  2. f(x)≡+∞f(x)\equiv+\infty; or
  3. f(x)≡−∞f(x)\equiv-\infty.
Proof

A function is proper if and only if it never takes the value −∞-\infty and is not identically +∞+\infty.

For improper functions, one can verify that f(x)≡+∞f(x)\equiv+\infty and f(x)≡−∞f(x)\equiv-\infty are convex conjugates of one another. Apart from these, if f(x)f(x) takes the value −∞-\infty at any point, then necessarily f∗(x∗)≡+∞f^*(x^*)\equiv+\infty. Thus these are the only improper functions satisfying f∗∗=ff^{**}=f. The rest of the proof concerns proper functions. For a proper function, lower semicontinuity and convexity are equivalent to its epigraph being a closed convex set.

Necessity is easy. Since f=f∗∗f=f^{**} is the convex conjugate of f∗f^*, it is the supremum of linear functions. The epigraph of f(x)f(x) is therefore an intersection of closed convex sets and is itself closed and convex. Hence every proper function satisfying f∗∗=ff^{**}=f is lower semicontinuous and convex.

Conversely, these conditions are sufficient. As in other strong-duality proofs, divide the proof into two steps.

First prove weak duality, f(x)≥f∗∗(x)f(x)\ge f^{**}(x). By the definition of the convex conjugate, for all x,x∗∈Rdx,x^*\in\mathbf R^d,

f∗(x∗)≥x∗⋅x−f(x).f^*(x^*) \ge x^*\cdot x-f(x).

Therefore, for all x,x∗∈Rdx,x^*\in\mathbf R^d,

f(x)≥x∗⋅x−f∗(x∗).f(x) \ge x^*\cdot x-f^*(x^*).

Taking the supremum over x∗x^* on the right gives f(x)≥f∗∗(x)f(x)\ge f^{**}(x).

Second, use the hyperplane separation theorem to show f(x)≤f∗∗(x)f(x)\le f^{**}(x). Suppose otherwise: for some x0∈Rdx_0\in\mathbf R^d, f(x0)>f∗∗(x0)f(x_0)>f^{**}(x_0). Since the epigraph epi⁡(f)\operatorname{epi}(f) is closed and convex and the singleton {(x0,f∗∗(x0))}\{(x_0,f^{**}(x_0))\} is compact and convex, the separation theorem gives (λ,t)∈Rd×R(\lambda,t)\in\mathbf R^d\times\mathbf R and α∈R\alpha\in\mathbf R such that for every x∈dom⁡f:={x∈Rd:f(x)<+∞}x\in\operatorname{dom} f:=\{x\in\mathbf R^d:f(x)<+\infty\} and every y≥f(x)y\ge f(x),

λ⋅x−ty<α<λ⋅x0−tf∗∗(x0)\lambda\cdot x-ty <\alpha <\lambda\cdot x_0 - tf^{**}(x_0)

Since yy can be arbitrarily large, t≥0t\ge 0. There are two cases.

First let t>0t>0. Divide through by tt, and set λ′=t−1λ\lambda'=t^{-1}\lambda and α′=t−1α\alpha'=t^{-1}\alpha:

λ′⋅x−y<α′<λ′⋅x0−f∗∗(x0).\lambda'\cdot x-y < \alpha'< \lambda'\cdot x_0-f^{**}(x_0).

For every x∈dom⁡fx\in\operatorname{dom} f, set y=f(x)y=f(x) to obtain

α′>λ′⋅x−f(x).\alpha' > \lambda'\cdot x - f(x).

Taking the supremum over xx on the right gives

α′≥sup⁡x∈Rdλ′⋅x−f(x)=f∗(λ′).\alpha' \ge \sup_{x\in\mathbf R^d}\lambda'\cdot x - f(x) = f^*(\lambda').

Hence

f∗∗(x0)<λ′⋅x0−f∗(λ′)≤sup⁡x∗∈Rdx∗⋅x0−f∗(x∗)=f∗∗(x0).f^{**}(x_0) < \lambda'\cdot x_0-f^*(\lambda') \le \sup_{x^*\in\mathbf R^d}x^*\cdot x_0-f^*(x^*) = f^{**}(x_0).

This contradiction rules out t>0t>0.

Finally let t=0t=0. We show that a perturbation converts this into the t>0t>0 case. Choose any λ0∈dom⁡f∗\lambda_0\in\operatorname{dom}f^*. By the definition of the convex conjugate, for every x∈dom⁡fx\in\operatorname{dom}f and y≥f(x)y\ge f(x),

λ0⋅x−y≤f∗(λ0).\lambda_0\cdot x-y\le f^*(\lambda_0).

Therefore, for every ε>0\varepsilon>0,

(λ+ελ0)⋅x−εy<α+εf∗(λ0).(\lambda+\varepsilon\lambda_0)\cdot x - \varepsilon y<\alpha+\varepsilon f^*(\lambda_0).

Since α<λ⋅x0\alpha<\lambda\cdot x_0, for sufficiently small ε>0\varepsilon>0 we also have

α+εf∗(λ0)<(λ+ελ0)⋅x0−εf∗∗(x0).\alpha+\varepsilon f^*(\lambda_0) < (\lambda+\varepsilon\lambda_0)\cdot x_0 - \varepsilon f^{**}(x_0).

Set λ′=λ+ελ0\lambda'=\lambda+\varepsilon\lambda_0, t′=εt'=\varepsilon, and α′=α+εf∗(λ0)\alpha'=\alpha+\varepsilon f^*(\lambda_0). Then

λ′⋅x−t′y<α′<λ′⋅x0−t′f∗∗(x0).\lambda'\cdot x-t'y <\alpha' <\lambda'\cdot x_0 - t'f^{**}(x_0).

This returns to the previous case and again gives a contradiction.

Therefore no x0∈Rdx_0\in\mathbf R^d satisfies f(x0)>f∗∗(x0)f(x_0)>f^{**}(x_0), so f(x0)≤f∗∗(x0)f(x_0)\le f^{**}(x_0) always. Combining the two steps gives f∗∗(x)=f(x)f^{**}(x)=f(x).

Therefore, strong duality holds if and only if v(y)v(y) is convex in y∈Rdy\in\mathbf R^d2.

Subgradients

The previous section showed that the value function h(λ)h(\lambda) of the penalized problem is the negative of the convex conjugate of the original value function v(y)v(y). Since the definition of a convex conjugate is itself a parameterized optimization problem, a result similar to the envelope theorem also holds. However, convex functions are not differentiable everywhere, so the definition of a derivative must first be extended to convex functions. This leads to the notion of a subgradient.

Subgradient

For a convex function f:Rd→R∪{±∞}f:\mathbf R^d\rightarrow\mathbf R\cup\{\pm\infty\} and x0∈dom⁡fx_0\in\operatorname{dom}f, if x∗∈Rdx^*\in\mathbf R^d satisfies

f(x)≥f(x0)+x∗⋅(x−x0),f(x) \ge f(x_0)+x^*\cdot(x-x_0),

for every x∈Rdx\in\mathbf R^d, then x∗x^* is a subgradient of f(x)f(x) at x0x_0. The set of all subgradients of f(x)f(x) at x0x_0 is its subdifferential, denoted ∂f(x0)\partial f(x_0).

Geometrically, the subdifferential of a convex function f(x)f(x) at x0x_0 is the set of slope vectors of all its supporting hyperplanes there. In one dimension,

∂f(x0)=[∂−f(x0),∂+f(x0)],\partial f(x_0) = [\partial_-f(x_0),\partial_+f(x_0)],

where ∂−f(x0)\partial_-f(x_0) and ∂+f(x0)\partial_+f(x_0) are the left and right derivatives of f(x)f(x) at x0x_0. For the extension f~(x)\tilde f(x) of a convex function f:Z→R∪{±∞}f:\mathbf Z\rightarrow\mathbf R\cup\{\pm\infty\} on the integers, its left and right derivatives at the integer point x=kx=k are the first differences on the two sides:

∂f~(k)=[f(k)−f(k−1),f(k+1)−f(k)].\partial\tilde f(k) = [f(k)-f(k-1),f(k+1)-f(k)].

Clearly, a convex function f(x)f(x) is differentiable at x0x_0 if and only if its subdifferential ∂f(x0)\partial f(x_0) is a singleton.

Because the convex conjugate maps slope vectors of supporting hyperplanes to their intercepts, it can determine whether a slope vector x∗x^* is a subgradient of f(x)f(x) at a given point xx.

Theorem (Convex Conjugates and Subgradients)

For a proper convex function f:Rd→Rf:\mathbf R^d\rightarrow\mathbf R and any x,x∗∈Rdx,x^*\in\mathbf R^d,

x∗∈∂f(x)  ⟺  x∗⋅x=f(x)+f∗(x∗).x^*\in\partial f(x) \iff x^*\cdot x = f(x) + f^*(x^*).

If ff is also lower semicontinuous, these two conditions are also equivalent to x∈∂f∗(x∗)x\in\partial f^*(x^*).

Proof

By the definition of a subgradient, x∗∈∂f(x)x^*\in\partial f(x) if and only if

f(x′)≥f(x)+x∗⋅(x′−x), ∀x′∈Rd.f(x') \ge f(x) + x^*\cdot(x'-x),~\forall x'\in\mathbf R^d.

This is equivalent to

x∗⋅x−f(x)≥x∗⋅x′−f(x′), ∀x′∈Rd.x^*\cdot x - f(x) \ge x^*\cdot x'-f(x'),~\forall x'\in\mathbf R^d.

Equivalently,

x∗⋅x−f(x)≥sup⁡x′∈Rdx∗⋅x′−f(x′)=f∗(x∗).x^*\cdot x - f(x) \ge \sup_{x'\in\mathbf R^d}x^*\cdot x'-f(x') = f^*(x^*).

By the definition of the convex conjugate, we always have

x∗⋅x−f(x)≤f∗(x∗).x^*\cdot x - f(x) \le f^*(x^*).

Thus the previous inequality is actually an equality, equivalently

x∗⋅x=f(x)+f∗(x∗).x^*\cdot x = f(x) + f^*(x^*).

This proves the first part.

If ff is a lower-semicontinuous proper convex function, the Fenchel–Moreau theorem gives f∗∗=ff^{**}=f. Thus the two conditions are equivalent to

x∗⋅x=f∗(x∗)+f∗∗(x).x^*\cdot x = f^*(x^*) + f^{**}(x).

Applying the first part again, they are equivalent to x∈∂f∗(x∗)x\in\partial f^*(x^*).

This result says that if f∗∗=ff^{**}=f, then the subdifferential ∂f∗(x∗)\partial f^{*}(x^*) of the convex conjugate f∗f^* at x∗x^* is exactly the set of xx-components of the intersections between the supporting hyperplanes with slope vector x∗x^* and the epigraph epi⁡f\operatorname{epi}f.

Corollary

For a lower-semicontinuous proper convex function f:Rd→Rf:\mathbf R^d\rightarrow\mathbf R and any x,x∗∈Rdx,x^*\in\mathbf R^d,

∂f(x)=arg⁡max⁡y∗∈Rdx⋅y∗−f∗(y∗),∂f∗(x∗)=arg⁡max⁡y∈Rdx∗⋅y−f(y).\begin{aligned} \partial f(x) &= \arg\max_{y^*\in\mathbf R^d} x\cdot y^* - f^*(y^*),\\ \partial f^*(x^*) &= \arg\max_{y\in\mathbf R^d} x^*\cdot y - f(y). \end{aligned}
Proof

We prove the second equality; the first is similar.

By the definition of the convex conjugate,

f∗(x∗)=sup⁡y∈Rdx∗⋅y−f(y),f^*(x^*) = \sup_{y\in\mathbf R^d} x^*\cdot y - f(y),

so

x∈arg⁡max⁡y∈Rdx∗⋅y−f(y)x \in \arg\max_{y\in\mathbf R^d} x^*\cdot y - f(y)

if and only if f∗(x∗)=x∗⋅x−f(x)f^*(x^*) = x^*\cdot x - f(x), and this equality holds if and only if x∈∂f∗(x∗)x\in\partial f^*(x^*). Thus the two sets are equal.

In the setting of this article, this result says that when solving

h(λ)=min⁡x∈Xf(x)−λ⋅g(x)=min⁡y∈g(X)v(y)−λ⋅yh(\lambda) = \min_{x\in X}f(x)-\lambda\cdot g(x) = \min_{y\in g(X)}v(y) - \lambda\cdot y

the values of the constraint function g(x)g(x) over the optimal decision set are exactly ∂(−h(λ))\partial(-h(\lambda)). When d=1d=1 and the problem involves only integers, this set is the interval

[h(λ−1)−h(λ),h(λ)−h(λ+1)].[h(\lambda-1)-h(\lambda),h(\lambda)-h(\lambda+1)].

For consecutive integer λ\lambda, these intervals meet end to end, so binary search only needs to compute one endpoint.

Convexity Proofs

The prerequisite for WQS binary search is convexity of the value function. In contests, convexity can be guessed by tabulation or intuition, but a rigorous proof is often difficult. This section uses the following classic problem to introduce common ways of proving convexity in algorithm contests.

Planting Trees

There are nn tree pits, and mm trees must be planted. Trees cannot be planted in adjacent pits. Given a length-nn sequence {ai}\{a_i\} representing the benefit of planting a tree in each pit, where benefits may be positive or negative, find the maximum possible total benefit of planting all mm trees.

In short, this is the problem of finding a maximum-weight independent set of size mm on a path of length nn.

These methods can roughly be divided into four categories:

  • Prove convexity of the value function by reducing to a convex optimization problem (including linear programming), for example by constructing a min-cost flow model;
  • Prove convexity inductively from the state transition, possibly using convexity-preserving transformations;
  • For interval-partition problems, verify that each interval cost function satisfies the quadrangle inequality;
  • For special problems, establish convexity directly by an exchange argument.

These proof methods are often connected to a corresponding solution method for the problem.

Reduction to Parametric Convex Optimization

Consider the parametric convex optimization problem

v(y)=inf⁡x∈D(y)f(x,y).v(y)=\inf_{x\in\mathcal D(y)} f(x,y).

The objective f:Rn×Rd→R∪{±∞}f:\mathbf R^n\times\mathbf R^d\rightarrow\mathbf R\cup\{\pm\infty\} is convex in x∈Rmx\in\mathbf R^m for every y∈Rdy\in\mathbf R^d, while the feasible-set mapping D:Rd→P(Rm)\mathcal D:\mathbf R^d\rightarrow \mathcal P(\mathbf R^m) is set-valued on Rd\mathbf R^d and has convex values D(y)\mathcal D(y) for every y∈Rdy\in\mathbf R^d. These conditions make this a convex optimization problem for every parameter y∈Rdy\in\mathbf R^d.

Theorem

Suppose the parametric convex optimization problem above satisfies:

  1. The objective f(x,y)f(x,y) is convex in (x,y)(x,y);
  2. The graph {(x,y):x∈D(y)}\{(x,y):x\in\mathcal D(y)\} of the feasible-set mapping y↦D(y)y\mapsto\mathcal D(y) is convex.

If v(y)>−∞v(y)>-\infty for every y∈Rdy\in\mathbf R^d, then the value function v(y)v(y) is a proper convex function of yy.

Proof

For any y1,y2∈Rdy_1,y_2\in\mathbf R^d and α∈(0,1)\alpha\in(0,1), we need to prove

v(αy1+(1−α)y2)≤αv(y1)+(1−α)v(y2).v(\alpha y_1+(1-\alpha)y_2) \le \alpha v(y_1) + (1-\alpha) v(y_2).

If v(y1)=+∞v(y_1)=+\infty or v(y2)=+∞v(y_2)=+\infty, the right-hand side is +∞+\infty and the inequality is immediate. Otherwise, v(y1)v(y_1) and v(y2)v(y_2) are both finite. For every ε>0\varepsilon>0 and i=1,2i=1,2, there exists xi∈D(yi)x_i\in\mathcal D(y_i) such that f(xi,yi)<v(yi)+εf(x_i,y_i)< v(y_i)+\varepsilon. Convexity of the graph of D\mathcal D gives

αx1+(1−α)x2∈D(αy1+(1−α)y2).\alpha x_1+(1-\alpha)x_2 \in \mathcal D(\alpha y_1+(1-\alpha)y_2).

Thus αx1+(1−α)x2\alpha x_1+(1-\alpha)x_2 is feasible for the optimization problem with parameter αy1+(1−α)y2\alpha y_1+(1-\alpha)y_2. By optimality and convexity of the objective,

v(αy1+(1−α)y2)≤f(αx1+(1−α)x2,αy1+(1−α)y2)≤αf(x1,y1)+(1−α)f(x2,y2)<αv(y1)+(1−α)v(y2)+ε.\begin{aligned} v(\alpha y_1+(1-\alpha)y_2) &\le f(\alpha x_1+(1-\alpha)x_2,\alpha y_1+(1-\alpha)y_2) \\ &\le \alpha f(x_1,y_1) + (1-\alpha)f(x_2,y_2) \\ &< \alpha v(y_1) + (1-\alpha) v(y_2) + \varepsilon. \end{aligned}

Since ε\varepsilon is arbitrary, let ε→0\varepsilon\rightarrow 0 to obtain

v(αy1+(1−α)y2)≤αv(y1)+(1−α)v(y2).v(\alpha y_1+(1-\alpha)y_2) \le \alpha v(y_1) + (1-\alpha) v(y_2).

Therefore the value function v(y)v(y) is convex.

The most common convex optimization problems in algorithm contests are linear programs.

Corollary

Let c∈Rnc\in\mathbf R^n, A1∈Rd1×nA_1\in\mathbf R^{d_1\times n}, A2∈Rd2×nA_2\in\mathbf R^{d_2\times n}, y1∈Rd1y_1\in\mathbf R^{d_1}, and y2∈Rd2y_2\in\mathbf R^{d_2}. Consider the parametric linear program

v(y1,y2)=min⁡x∈Rnc⋅x subject to A1x≤y1,A2x=y2,x≥0.v(y_1,y_2)=\min_{x\in\mathbf R^n} c\cdot x \text{ subject to }A_1x\le y_1,A_2x=y_2,x\ge 0.

Then the value function v(y1,y2)v(y_1,y_2) is convex in (y1,y2)(y_1,y_2).

Whether the constraints are inequalities or equalities, the value function of a linear program is convex in the parameters of the constraints.

Many graph problems can be written as linear programs:

  • Network-flow problems: maximum flow, minimum cut, and minimum-cost flow;
  • Shortest paths with no negative cycles;
  • Maximum (weight) matching and minimum vertex cover in bipartite graphs;
  • Maximum (weight) matching in general graphs;
  • Minimum spanning tree3.

Therefore, the value functions of these problems are convex (or concave) functions of their parameters.

Integer Constraints

When graph models are used to model real problems, implicit integer constraints often exist, such as an edge being either selected or not selected and flow being integral. Thus they become integer linear programs (integer linear programming, ILP), rather than linear programs (LP). Since ILP is not a convex optimization problem, its value function need not be convex in the problem parameters. Relaxing the integer constraints in an ILP gives an LP, but the latter need not have an optimal solution satisfying the integer constraints. Therefore, the optimal value of the relaxed LP may be strictly better than that of the corresponding ILP, and the two need not be equivalent.

The graph problems listed above can all be written as LPs without integer constraints. For other problems, such as maximum independent set in a general graph, integer constraints are necessary. Moreover, even if a graph problem can be written as an LP, adding extra linear constraints may destroy the equivalence between the corresponding ILP and LP, so the constrained graph problem may no longer have an LP formulation.

For example, in the context of min-cost flow, we have the following familiar result:

Corollary

In the min-cost flow model, the minimum cost v(m)v(m) is a convex function of the flow mm.

Proof

Let G=(V,E)G=(V,E) be a directed graph. Let edge (i,j)(i,j) have capacity cijc_{ij} and per-unit cost wijw_{ij}, with source ss and sink tt. Let the decision variables be {fij}\{f_{ij}\}, where fijf_{ij} is the flow on edge (i,j)∈E(i,j)\in E. The min-cost flow can be written as the linear program

v(m)=min⁡{fij}  ∑(i,j)∈Ewijfijsubject to ∑(j,i)∈Efji−∑(i,j)∈Efij={−m,i=s,m,i=t,0,otherwise, ∀i∈V,0≤fij≤cij, ∀(i,j)∈E.\begin{aligned} v(m)=\min_{\{f_{ij}\}}\;&\sum_{(i,j)\in E}w_{ij}f_{ij}\\ \text{subject to }&\sum_{(j,i)\in E}f_{ji} - \sum_{(i,j)\in E}f_{ij} = \begin{cases} -m, & i=s,\\ m, & i=t,\\ 0, & \text{otherwise}, \end{cases} ~\forall i\in V,\\ &0\le f_{ij}\le c_{ij},~\forall (i,j)\in E. \end{aligned}

Thus the minimum cost v(m)v(m) is convex in the parameter mm.

Many problems in algorithm contests can be reduced to network flow or other graph problems, and their value-function convexity can be proved similarly.

This method gives the first convexity proof for the tree-planting problem:

Convexity Proof 1

The maximum benefit in the tree-planting problem can be obtained from the following maximum-cost maximum-flow model:

  • From source ss, add an edge to vertex rr with capacity mm and cost 00;
  • From vertex rr, add an edge of capacity 11 and cost 00 to every odd vertex i=1,3,⋯ ,2⌈n/2⌉−1i=1,3,\cdots,2\lceil n/2\rceil-1;
  • From every even vertex i=0,2,⋯ ,2⌊n/2⌋i=0,2,\cdots,2\lfloor n/2\rfloor, add an edge of capacity 11 and cost 00 to sink tt;
  • For each i=1,⋯ ,ni=1,\cdots,n, add an edge of capacity 11 and cost aia_i from the odd vertex among i−1i-1 and ii to the even vertex.

The final answer is the maximum cost found. Converting this graph model into the corresponding linear program (see the proof of the corollary above), the total flow mm appears in the inequality representing the flow constraint on edge (s,r)(s,r). The corollary shows that the maximum cost v(m)v(m) is concave in the flow mm.

Using this cost-flow model, the problem can be solved in O(nlog⁡n)O(n\log n) time by simulating cost flow or using regretful greedy.

Using the State Transition

Although a state transition may not provide an effective way to compute the answer, it can often prove that the state function f(i,j)f(i,j) is convex in the parameter jj. Specifically, regard f(i,⋅)f(i,\cdot) as the state at ii, and view the transition for f(i,j)f(i,j) as a recurrence for f(i,⋅)f(i,\cdot). Convexity of every f(i,⋅)f(i,\cdot) can then be proved inductively. This method is especially common in Slope Trick DP optimization, which also discusses common convexity-preserving transformations.

The same method proves convexity for the tree-planting problem:

Convexity Proof 2

Let f(i,j)f(i,j) be the maximum benefit when jj trees are planted in the first ii pits. Consider

f(i,j)=max⁡{f(i−1,j),f(i−2,j−1)+ai}.f(i,j) = \max\{f(i-1,j),f(i-2,j-1)+a_i\}.

View this transition as a recurrence for f(i,⋅)f(i,\cdot). Because the maximum involves two different functions, it cannot be expressed as a supremal convolution. Nevertheless, induction can still prove that f(i,⋅)f(i,\cdot) is concave.

Specifically, prove inductively that:

  • f(i,j)−f(i−2,j−1)f(i,j)-f(i-2,j-1) is decreasing in jj;
  • f(i,j)−f(i−1,j)f(i,j)-f(i-1,j) is increasing in jj.

The induction base is trivial. Assume these statements hold for all i−1i-1 and earlier indices; now prove them for ii. Direct verification is enough.

First, by the induction hypothesis,

f(i−1,j)−f(i−2,j−1)=(f(i,j)−f(i−2,j−1))−(f(i,j)−f(i−1,j))f(i-1,j) - f(i-2,j-1) = (f(i,j)-f(i-2,j-1)) - (f(i,j)-f(i-1,j))

is decreasing in jj. Therefore,

f(i,j)−f(i−2,j−1)=max⁡{f(i−1,j)−f(i−2,j−1),ai}f(i,j) - f(i-2,j-1) = \max\{f(i-1,j) - f(i-2,j-1), a_i\}

is decreasing in jj, and

f(i,j)−f(i−1,j)=max⁡{0,ai−(f(i,j)−f(i−2,j−1))}f(i,j) - f(i-1,j) = \max\{0,a_i-(f(i,j) - f(i-2,j-1))\}

is increasing in jj. This completes the induction.

Consequently,

f(i,j)−f(i,j−1)=(f(i,j)−f(i−2,j−1))−(f(i,j−1)−f(i−1,j−1))−(f(i−1,j−1)−f(i−2,j−1))f(i,j) - f(i,j-1) = (f(i,j)-f(i-2,j-1)) - (f(i,j-1) - f(i-1,j-1)) - (f(i-1,j-1) - f(i-2,j-1))

is decreasing in jj. Thus f(i,j)f(i,j) is concave in jj, and the value function v(m)=f(n,m)v(m)=f(n,m) is concave in mm.

One by-product of this proof is that for every ii there is a pip_i such that

f(i,j)={f(i−1,j),j≤pi,f(i−2,j−1)+ai,j>pi.f(i,j) = \begin{cases} f(i-1,j), & j\le p_i,\\ f(i-2,j-1) + a_i, & j> p_i. \end{cases}

This means that the sequence f(i,⋅)f(i,\cdot) can be maintained directly with a balanced tree in O(nlog⁡2n)O(n\log^2n) time. The benefit is that arbitrary tree-spacing conditions can be handled and all values of v(m)v(m) are obtained at once.

Quadrangle Inequality

Another common class of problems with convex value functions in algorithm contests is interval partitioning. That page proves that if the cost function of a single interval satisfies the quadrangle inequality, then the minimum cost of an interval partition with a bounded number of intervals is convex in the number of intervals. It also gives methods for determining whether a function w(l,r)w(l,r) satisfies the quadrangle inequality. The most direct method is to compute its mixed second difference:

ΔlΔrw(l,r)=Δl(w(l,r+1)−w(l,r))=w(l+1,r+1)−w(l+1,r)−w(l,r+1)+w(l,r).\begin{aligned} \Delta_l \Delta_r w(l,r) &= \Delta_l(w(l,r+1)-w(l,r)) \\ &= w(l+1,r+1)-w(l+1,r)-w(l,r+1)+w(l,r). \end{aligned}

The function w(l,r)w(l,r) satisfies the quadrangle inequality if and only if ΔlΔrw(l,r)\Delta_l \Delta_r w(l,r) is nonpositive. Intuitively, this usually means that expanding an interval in both directions—moving the left endpoint left and the right endpoint right—has some synergistic effect.

The tree-planting problem can also be viewed as interval partitioning, so its convexity can be proved by verifying the quadrangle inequality.

Convexity Proof 3

Add an arbitrary a0a_0 before the tree-benefit sequence. The tree-planting problem is then equivalent to partitioning {a0,a1,⋯ ,an}\{a_0,a_1,\cdots,a_n\} into mm segments, where the benefit function of each segment is

w(l,r)=max⁡i∈[l+1,r]aiw(l,r) = \max_{i\in[l+1,r]} a_i

This is an interval-partition problem. The benefit of each segment is the maximum benefit of the trees other than the first tree, which guarantees that planted trees are separated.

Because this is a maximization problem, verify “crossing greater than containment”: for every a

w(a,c)+w(b,d)≥w(a,d)+w(b,c).w(a,c)+w(b,d) \ge w(a,d)+w(b,c).

Substitute the benefit function and set

A=max⁡i∈[a+1,b]ai, B=max⁡i∈[b+1,c]ai, C=max⁡i∈[c+1,d]ai,A = \max_{i\in[a+1,b]} a_i,~ B = \max_{i\in[b+1,c]} a_i,~ C = \max_{i\in[c+1,d]}a_i,

The desired inequality becomes

max⁡{A,B}+max⁡{B,C}≥max⁡{A,B,C}+B.\max\{A,B\} + \max\{B,C\} \ge \max\{A,B,C\} + B.

The larger of max⁡{A,B}\max\{A,B\} and max⁡{B,C}\max\{B,C\} on the left is max⁡{A,B,C}\max\{A,B,C\}, while the smaller is always at least BB, so the inequality holds.

After converting the tree-planting problem into interval partitioning, precompute interval extrema with an ST table or similar method. Then a single interval cost can be computed in O(1)O(1), and the interval-partition algorithm solves the problem in O(nlog⁡nlog⁡L)O(n\log n\log L) or O(n(n+m))O(n(n+m)) time. The method also handles arbitrary tree-spacing conditions.

Exchange Argument

An exchange argument is often used to prove convexity of value functions in combinatorial optimization problems. Specifically, start from optimal solutions for parameters m−1m-1 and m+1m+1, exchange some elements to construct a feasible solution for parameter mm with value at most (v(m−1)+v(m+1))/2(v(m-1)+v(m+1))/2, and use optimality of v(m)v(m) to prove convexity. Unlike convex optimization, combinatorial optimization has no natural way to construct an intermediate form of two solutions, so exchange arguments usually require some ingenuity.

“Increasing Marginal Cost” Does Not Necessarily Imply Convexity

Objective functions in combinatorial optimization often have increasing-marginal-cost properties, but this does not necessarily imply convexity. A typical example is [IOI 2005] River: its path version satisfies the quadrangle inequality and is therefore convex, but the tree version has cases where convexity fails.

A common property characterizing increasing marginal cost is supermodularity. For a function f:PX→Rf:\mathcal PX\rightarrow\mathbf R on the family of subsets PX\mathcal PX of a finite set XX, if it satisfies either of the following equivalent properties:

  1. (Crossing less than containment) For all subsets A,B⊆XA,B\subseteq X, f(A)+f(B)≤f(A∪B)+f(A∩B)f(A)+f(B) \le f(A\cup B) + f(A\cap B);
  2. (Increasing marginal cost) For all A⊆B⊆XA\subseteq B\subseteq X and x∈X∖Bx\in X\setminus B, f(A∪{x})−f(A)≤f(B∪{x})−f(B)f(A\cup\{x\})-f(A)\le f(B\cup\{x\})-f(B);

then ff is supermodular. However, in an optimization problem with a supermodular objective, the value function

v(m)=min⁡A⊆Xf(A) subject to ∣A∣=mv(m) = \min_{A\subseteq X} f(A) \text{ subject to }|A|=m

need not be convex in mm. The reason is that optimal solutions for subset sizes m−1m-1 and m+1m+1 generally cannot be used to construct a feasible subset of size mm satisfying the required value relation.

The exchange argument provides another proof of convexity for the tree-planting problem.

Convexity Proof 4

Use an exchange argument. Let optimal solutions for planting m−1m-1 and m+1m+1 trees be {xi(m−1)}∈{0,1}n\{x_i^{(m-1)}\}\in\{0,1\}^n and {xi(m+1)}∈{0,1}n\{x_i^{(m+1)}\}\in\{0,1\}^n, respectively. A value of 11 means a tree is planted in the pit, and 00 means no tree is planted. Define {zi}∈{0,±1}n\{z_i\}\in\{0,\pm 1\}^n by

zi=xi(m+1)−xi(m−1), i=1,⋯ ,n.z_i = x_i^{(m+1)} - x_i^{(m-1)},~i=1,\cdots,n.

This sequence marks the differences between the two planting schemes. A value of 00 means that the pit either has a tree in both schemes or has no tree in either; −1-1 and +1+1 mean that the pit has a tree only in x(m−1)x^{(m-1)} or only in x(m+1)x^{(m+1)}, respectively. Since no scheme plants trees in adjacent pits:

  • In every consecutive nonzero segment, the values of ziz_i alternate between ±1\pm 1;
  • The 00 values immediately outside a maximal consecutive nonzero segment mean that neither scheme plants a tree there.

If the sum of ziz_i over a maximal consecutive nonzero segment is +1+1, then scheme x(m+1)x^{(m+1)} plants one more tree than scheme x(m−1)x^{(m-1)} in that segment, so we can exchange the planting positions of the two schemes within the segment. This produces two feasible schemes, each planting mm trees. The total planting positions and count across the two schemes are unchanged, only redistributed, so their total benefit remains v(m−1)+v(m+1)v(m-1)+v(m+1). These two mm-tree schemes need not be optimal, so each has benefit at most v(m)v(m). Hence

v(m−1)+v(m+1)≤2v(m),v(m-1) + v(m+1) \le 2v(m),

which means v(m)v(m) is concave in mm.

It remains to show that a maximal consecutive nonzero segment with sum +1+1 exists. Since it is a sum of alternating ±1\pm 1, the sum of each such segment is 00 or ±1\pm 1. The sum of all maximal consecutive nonzero segments is 22, so at least two segments have sum exactly +1+1. This completes the proof.

Example Problems

This section presents examples applying WQS binary search in different settings.

Template Problem

Luogu P1484 Planting Trees

There are nn tree pits, and at most mm trees may be planted. Trees cannot be planted in adjacent pits. Given a length-nn sequence {ai}\{a_i\} representing the benefit of planting in each pit, where benefits may be positive or negative, find the maximum possible total benefit of planting these mm trees.

Solution

Unlike the tree-planting problem discussed above, this problem asks for at most mm trees rather than exactly mm. Let v(m)v(m) still denote the value function of the earlier problem. The answer here is v~(m)=max⁡k≤mv(k)\tilde v(m)=\max_{k\le m}v(k). Since v(m)v(m) is concave, hence unimodal, the answer keeps only the part of v(m)v(m) rising to its peak and then remains at the peak. Equivalently, it retains only the part with nonnegative tangent slope. Thus the only difference from the earlier problem is that the initial slope range for WQS binary search is [0,max⁡iai][0,\max_ia_i] rather than [min⁡iai,max⁡iai][\min_ia_i,\max_ia_i]. After WQS binary search removes the cardinality constraint, the problem becomes maximum-weight independent set on a path, with the original profits {ai}\{a_i\} replaced by {ai+k}\{a_i+k\}. This is a classic dynamic programming problem. Let f(i,j)f(i,j) be the maximum profit of the subproblem on the first ii pits when the j=1j=1 state means planting and the j=0j=0 state means leaving the ii-th pit empty. The transition is

f(i,0)=max⁡{f(i−1,0),f(i−1,1)},f(i,1)=f(i−1,0)+ai+k.\begin{aligned} f(i,0) &= \max\{f(i-1,0),f(i-1,1)\},\\ f(i,1) &= f(i-1,0) + a_i + k. \end{aligned}

The initial conditions are f(0,0)=0f(0,0)=0 and f(0,1)=−∞f(0,1)=-\infty, and the final answer is max⁡{f(n,0),f(n,1)}\max\{f(n,0),f(n,1)\}. One computation takes O(n)O(n) time, so the total complexity is O(nlog⁡L)O(n\log L), where L=max⁡i∣ai∣L=\max_i|a_i|.

A reference implementation follows:

Traditional method

#include <algorithm>
#include <cstring>
#include <iostream>
#include <tuple>
#include <vector>

int main() {
  int n, m;
  std::cin >> n >> m;
  std::vector<int> a(n + 1);
  for (int i = 1; i <= n; ++i) std::cin >> a[i];
  // Calculate h(k) = max_x f(x) - k * g(x).
  // Meanwhile, obtain the maximum value g(x) of the optimizer x.
  auto calc = [&](int k) -> std::pair<long long, int> {
    long long dp[2] = {0, -0x3f3f3f3f3f3f3f3f};
    int opt[2] = {0, 0};
    for (int i = 1; i <= n; ++i) {
      long long tmp_dp[2];
      int tmp_opt[2];
      if (dp[0] > dp[1]) {
        tmp_dp[0] = dp[0];
        tmp_opt[0] = opt[0];
      } else if (dp[1] > dp[0]) {
        tmp_dp[0] = dp[1];
        tmp_opt[0] = opt[1];
      } else {
        tmp_dp[0] = dp[0];
        tmp_opt[0] = std::max(opt[0], opt[1]);
      }
      tmp_dp[1] = dp[0] + a[i] - k;
      tmp_opt[1] = opt[0] + 1;
      std::memcpy(dp, tmp_dp, sizeof(dp));
      std::memcpy(opt, tmp_opt, sizeof(opt));
    }
    long long val;
    int opt_m;
    if (dp[0] > dp[1]) {
      val = dp[0];
      opt_m = opt[0];
    } else if (dp[1] > dp[0]) {
      val = dp[1];
      opt_m = opt[1];
    } else {
      val = dp[0];
      opt_m = std::max(opt[0], opt[1]);
    }
    return {val, opt_m};
  };
  // WQS binary search.
  long long val, tar_val;
  int opt_m, tar_k;
  std::tie(val, opt_m) = calc(0);
  if (opt_m <= m) {
    // Have already reached the peak.
    tar_k = 0;
    tar_val = val;
  } else {
    // Find the maximum k such that g(x) >= m.
    int ll = 0, rr = 1000000;
    while (ll <= rr) {
      int mm = (ll + rr) / 2;
      std::tie(val, opt_m) = calc(mm);
      if (opt_m >= m) {
        tar_k = mm;
        tar_val = val;
        ll = mm + 1;
      } else {
        rr = mm - 1;
      }
    }
  }
  long long res = tar_val + (long long)tar_k * m;
  std::cout << res << std::endl;
  return 0;
}

Dual method

#include <algorithm>
#include <iostream>
#include <tuple>
#include <type_traits>
#include <vector>

// Golden section search on integer domain (unimodal function)
template <typename T, typename F>
typename std::enable_if<
    std::is_integral<T>::value,
    std::pair<T, decltype(std::declval<F>()(std::declval<T>()))>>::type
golden_section_search(T ll, T rr, F func) {
  constexpr long double phi = 0.618033988749894848204586834L;
  T ml = ll + static_cast<T>((rr - ll) * (1 - phi));
  T mr = ll + static_cast<T>((rr - ll) * phi);
  auto fl = func(ml), fr = func(mr);
  while (ml < mr) {
    if (fl > fr) {
      rr = mr;
      mr = ml;
      fr = fl;
      ml = ll + static_cast<T>((rr - ll) * (1 - phi));
      fl = func(ml);
    } else {
      ll = ml;
      ml = mr;
      fl = fr;
      mr = ll + static_cast<T>((rr - ll) * phi);
      fr = func(mr);
    }
  }
  T best_x = ll;
  auto best_val = func(ll);
  for (T i = ll + 1; i <= rr; ++i) {
    auto val = func(i);
    if (val > best_val) {
      best_val = val;
      best_x = i;
    }
  }
  return {best_x, best_val};
}

int main() {
  int n, m;
  std::cin >> n >> m;
  std::vector<int> a(n + 1);
  for (int i = 1; i <= n; ++i) std::cin >> a[i];
  // Calculate h(k) = max_x f(x) + k * g(x).
  auto calc = [&](int k) -> long long {
    long long dp[2] = {0, -0x3f3f3f3f3f3f3f3f};
    for (int i = 1; i <= n; ++i) {
      std::tie(dp[0], dp[1]) =
          std::make_pair(std::max(dp[0], dp[1]), dp[0] + a[i] + k);
    }
    return std::max(dp[0], dp[1]);
  };
  // Solve the dual problem to find v(m).
  // Implemented as a minimization problem by adding negative signs.
  // Only consider tangent lines of negative slopes to ignore the part
  //     of the curve after the peak.
  auto res = -golden_section_search(-1000000, 0, [&](int k) -> long long {
                return -calc(k) + (long long)k * m;
              }).second;
  std::cout << res << std::endl;
  return 0;
}
Luogu P2619 [National Training Team] Tree I

Given a weighted undirected connected graph whose edges are black or white, find the minimum-weight spanning tree containing exactly mm white edges.

Solution

First, an exchange argument proves that v(m)v(m) is convex. Assume without loss of generality that all edge weights are distinct. Cases in which two edge weights are equal can be perturbed so that all edge weights become distinct; letting the perturbation tend to zero then shows that convexity still holds in the limiting case, namely when two edge weights are equal. The key to the proof is the following lemma:4

Lemma

Let SS and TT be two spanning trees of the undirected connected graph G=(V,E)G=(V,E). For any e∈S∖Te\in S\setminus T, there exists at least one edge f∈T∖Sf\in T\setminus S such that both S−e+fS-e+f and T−f+eT-f+e are spanning trees of GG.

Proof

Let e=(u,v)e=(u,v), and let PP be the unique path in tree TT connecting uu and vv. Since P+eP+e is the unique cycle in T+eT+e, deleting any edge on PP, say ff, makes T−f+eT-f+e a spanning tree. Meanwhile, S−eS-e is a forest with two connected components, whose vertex sets are V1V_1 and V2V_2. Therefore, choosing an edge f∈Pf\in P such that the edge ff connects V1V_1 and V2V_2 guarantees that S−e+fS-e+f is a spanning tree. Such an edge ff always exists because uu and vv belong to V1V_1 and V2V_2, respectively, and PP connects uu and vv. Moreover, f∉Sf\notin S, because in S−eS-e, V1V_1 and V2V_2 are disconnected. This completes the proof.

Let Tm−1T_{m-1} and Tm+1T_{m+1} be minimum spanning trees with m−1m-1 and m+1m+1 white edges, respectively. Let ee be a white edge in Tm+1∖Tm−1T_{m+1}\setminus T_{m-1}. Applying the lemma shows that there exists an edge f∈Tm−1∖Tm+1f\in T_{m-1}\setminus T_{m+1} such that T′=Tm+1−e+fT'=T_{m+1}-e+f and T′′=Tm−1+e−fT''=T_{m-1}+e-f are both spanning trees. Since only one pair of edges is exchanged, the sum of the edge weights of T′T' and T′′T'' remains v(m−1)+v(m+1)v(m-1)+v(m+1). We now consider two cases:

  • If ff is a black edge, then both T′T' and T′′T'' have mm white edges. The edge-weight sum of each is at least v(m)v(m). This proves 2v(m)≤v(m−1)+v(m+1)2v(m)\le v(m-1)+v(m+1), and hence v(m)v(m) is convex as a function of mm.
  • If ff is a white edge, then T′T' and T′′T'' have m+1m+1 and m−1m-1 white edges, respectively, so their edge-weight sums are at least v(m+1)v(m+1) and v(m−1)v(m-1), respectively. However, we already know that their sums equal v(m−1)+v(m+1)v(m-1)+v(m+1). This implies that the edge-weight sum of T′T' is exactly v(m+1)v(m+1). Comparing T′T' with Tm+1T_{m+1} shows that ee and ff must have equal weights. This contradicts the assumption, so this case cannot occur.

This proves that v(m)v(m) is convex as a function of mm.

Once the convexity of v(m)v(m) has been established, the problem can be solved with WQS binary search. Remove the cardinality constraint, subtract kk from the weight of every white edge, and solve the minimum spanning tree problem. For this, apply Kruskal's algorithm. Using a disjoint-set union to maintain connectivity, the complexity is O(Elog⁡E+Eα(V))O(E\log E+E\alpha(V)), where EE and VV are the numbers of edges and vertices, respectively, and α(⋅)\alpha(\cdot) is the inverse Ackermann function. The main part of the complexity, O(Elog⁡E)O(E\log E), is the cost of sorting the edges, and it can be optimized further in this problem. Although the WQS binary search requires computing a minimum spanning tree multiple times, only the weights of white edges are shifted by the same amount each time. Therefore, we can sort the white and black edges separately during preprocessing, and when computing each minimum spanning tree, we only need to merge the white and black edges after adjusting their weights. This reduces the total complexity to O(Elog⁡E+Eα(V)log⁡L)O(E\log E+E\alpha(V)\log L), where LL is the length of the range of possible edge weights.

A reference implementation follows:

Traditional method

#include <algorithm>
#include <array>
#include <iostream>
#include <numeric>
#include <tuple>
#include <vector>

class DisjointSet {
  std::vector<int> fa, sz;

  int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

 public:
  DisjointSet(int n) : fa(n), sz(n, 1) { std::iota(fa.begin(), fa.end(), 0); }

  bool unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return false;
    if (sz[x] < sz[y]) std::swap(x, y);
    fa[y] = x;
    sz[x] += sz[y];
    return true;
  }
};

int main() {
  int V, E, m;
  std::cin >> V >> E >> m;
  std::array<std::vector<std::array<int, 3>>, 2> edges;
  edges[0].reserve(E);  // white edges.
  edges[1].reserve(E);  // black edges.
  for (int i = 0; i < E; ++i) {
    int u, v, c, w;
    std::cin >> u >> v >> w >> c;
    edges[c].push_back({u, v, w});
  }
  // Sort edges.
  std::sort(edges[0].begin(), edges[0].end(),
            [&](const auto& lhs, const auto& rhs) -> bool {
              return lhs[2] < rhs[2];
            });
  std::sort(edges[1].begin(), edges[1].end(),
            [&](const auto& lhs, const auto& rhs) -> bool {
              return lhs[2] < rhs[2];
            });
  // Calculate h(k) = min_x f(x) - k * g(x) by Kruskal algorithm.
  // Use white edges first, whenever possible.
  auto calc = [&](int k) -> std::pair<int, int> {
    int res = 0, cnt = 0;
    DisjointSet djs(V);
    int i[2] = {};
    while (i[0] < edges[0].size() && i[1] < edges[1].size()) {
      int c = edges[0][i[0]][2] - k > edges[1][i[1]][2];
      if (djs.unite(edges[c][i[c]][0], edges[c][i[c]][1])) {
        res += edges[c][i[c]][2] - (c ? 0 : k);
        cnt += c ? 0 : 1;
      }
      ++i[c];
    }
    while (i[0] < edges[0].size()) {
      if (djs.unite(edges[0][i[0]][0], edges[0][i[0]][1])) {
        res += edges[0][i[0]][2] - k;
        ++cnt;
      }
      ++i[0];
    }
    while (i[1] < edges[1].size()) {
      if (djs.unite(edges[1][i[1]][0], edges[1][i[1]][1])) {
        res += edges[1][i[1]][2];
      }
      ++i[1];
    }
    return {res, cnt};
  };
  // WQS binary search.
  // Find the minimum k such that g(x) >= m.
  int val, opt_m, tar_val, tar_k;
  int ll = -100, rr = 100;
  while (ll <= rr) {
    int mm = ll + (rr - ll) / 2;
    std::tie(val, opt_m) = calc(mm);
    if (opt_m >= m) {
      tar_val = val;
      tar_k = mm;
      rr = mm - 1;
    } else {
      ll = mm + 1;
    }
  }
  int res = tar_val + tar_k * m;
  std::cout << res << std::endl;
  return 0;
}

Dual method

#include <algorithm>
#include <array>
#include <iostream>
#include <numeric>
#include <tuple>
#include <type_traits>
#include <vector>

// Golden section search on integer domain (unimodal function)
template <typename T, typename F>
typename std::enable_if<
    std::is_integral<T>::value,
    std::pair<T, decltype(std::declval<F>()(std::declval<T>()))>>::type
golden_section_search(T ll, T rr, F func) {
  constexpr long double phi = 0.618033988749894848204586834L;
  T ml = ll + static_cast<T>((rr - ll) * (1 - phi));
  T mr = ll + static_cast<T>((rr - ll) * phi);
  auto fl = func(ml), fr = func(mr);
  while (ml < mr) {
    if (fl > fr) {
      rr = mr;
      mr = ml;
      fr = fl;
      ml = ll + static_cast<T>((rr - ll) * (1 - phi));
      fl = func(ml);
    } else {
      ll = ml;
      ml = mr;
      fl = fr;
      mr = ll + static_cast<T>((rr - ll) * phi);
      fr = func(mr);
    }
  }
  T best_x = ll;
  auto best_val = func(ll);
  for (T i = ll + 1; i <= rr; ++i) {
    auto val = func(i);
    if (val > best_val) {
      best_val = val;
      best_x = i;
    }
  }
  return {best_x, best_val};
}

class DisjointSet {
  std::vector<int> fa, sz;

  int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

 public:
  DisjointSet(int n) : fa(n), sz(n, 1) { std::iota(fa.begin(), fa.end(), 0); }

  bool unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return false;
    if (sz[x] < sz[y]) std::swap(x, y);
    fa[y] = x;
    sz[x] += sz[y];
    return true;
  }
};

int main() {
  int V, E, m;
  std::cin >> V >> E >> m;
  std::array<std::vector<std::array<int, 3>>, 2> edges;
  edges[0].reserve(E);  // white edges.
  edges[1].reserve(E);  // black edges.
  for (int i = 0; i < E; ++i) {
    int u, v, c, w;
    std::cin >> u >> v >> w >> c;
    edges[c].push_back({u, v, w});
  }
  // Sort edges.
  std::sort(edges[0].begin(), edges[0].end(),
            [&](const auto& lhs, const auto& rhs) -> bool {
              return lhs[2] < rhs[2];
            });
  std::sort(edges[1].begin(), edges[1].end(),
            [&](const auto& lhs, const auto& rhs) -> bool {
              return lhs[2] < rhs[2];
            });
  // Calculate h(k) = min_x f(x) - k * g(x) by Kruskal algorithm.
  auto calc = [&](int k) -> int {
    int res = 0;
    DisjointSet djs(V);
    int i[2] = {};
    while (i[0] < edges[0].size() && i[1] < edges[1].size()) {
      int c = edges[0][i[0]][2] - k > edges[1][i[1]][2];
      if (djs.unite(edges[c][i[c]][0], edges[c][i[c]][1])) {
        res += edges[c][i[c]][2] - (c ? 0 : k);
      }
      ++i[c];
    }
    while (i[0] < edges[0].size()) {
      if (djs.unite(edges[0][i[0]][0], edges[0][i[0]][1])) {
        res += edges[0][i[0]][2] - k;
      }
      ++i[0];
    }
    while (i[1] < edges[1].size()) {
      if (djs.unite(edges[1][i[1]][0], edges[1][i[1]][1])) {
        res += edges[1][i[1]][2];
      }
      ++i[1];
    }
    return res;
  };
  // Solve the dual problem to find v(m).
  auto res = golden_section_search(-100, 100, [&](int k) -> int {
               return calc(k) + k * m;
             }).second;
  std::cout << res << std::endl;
  return 0;
}

Interval Partition Problem

Luogu P6246 [IOI 2000] Enhanced Post Office Enhanced

Given an increasing sequence of nn positive integers {ai}\{a_i\} representing the positions of nn villages beside a highway, build mm post offices. The positions of the post offices should be chosen to minimize the sum of the distances from all villages to their nearest post office. Find this minimum.

Solution

This is a typical interval partition problem. See that page for implementation details of the monotone queue.

Each post office serves the villages closest to it, which must form a contiguous block along the highway. Building mm post offices is therefore equivalent to partitioning the villages into mm contiguous segments and building the cheapest possible post office for each segment. The post office for a segment should be placed at the median village. Thus the cost function for [l,r][l,r] is

w(l,r)=∑i=lr∣ai−a⌊(l+r)/2⌋∣.w(l,r) = \sum_{i=l}^r|a_i-a_{\lfloor(l+r)/2\rfloor}|.

It satisfies the quadrangle inequality because its mixed second difference is nonpositive:

ΔlΔrw(l,r)=Δl(ar+1−a⌊(l+r+1)/2⌋)=a⌊(l+r+1)/2⌋−a⌊(l+r+2)/2⌋≤0.\Delta_l\Delta_r w(l,r) = \Delta_l(a_{r+1} - a_{\lfloor(l+r+1)/2\rfloor}) = a_{\lfloor(l+r+1)/2\rfloor}-a_{\lfloor(l+r+2)/2\rfloor} \le 0.

Therefore, combining a monotone queue with WQS binary search solves the problem in O(nlog⁡nlog⁡L)O(n\log n\log L) time.

A reference implementation follows:

Traditional method

#include <cmath>
#include <deque>
#include <iostream>
#include <tuple>
#include <type_traits>
#include <vector>

// Monotone decision DP.
// This solves f(i) = min f(j-1) + w(j,i) s.t. 1 <= j <= i.
// Also records the minimal optimal decision j for each f(i).
template <typename W>
std::pair<std::vector<decltype(std::declval<W>()(0, 0))>, std::vector<int>>
monotone_decision_opt_dp(int n, W ww) {
  using ValueT = decltype(std::declval<W>()(0, 0));
  std::vector<ValueT> f(n + 1);
  std::vector<int> opt(n + 1), lt(n + 1), rt(n + 1);
  std::deque<int> dq;
  auto w = [&](int j, int i) -> ValueT { return ww(j, i) + f[j - 1]; };
  for (int j = 1; j <= n; ++j) {
    if (!dq.empty() && rt[dq.front()] < j) dq.pop_front();
    if (!dq.empty()) lt[dq.front()] = j;
    while (!dq.empty() && w(j, lt[dq.back()]) < w(dq.back(), lt[dq.back()])) {
      dq.pop_back();
    }
    if (dq.empty()) {
      lt[j] = j;
      rt[j] = n;
      dq.emplace_back(j);
    } else if (w(j, rt[dq.back()]) >= w(dq.back(), rt[dq.back()])) {
      if (rt[dq.back()] < n) {
        lt[j] = rt[dq.back()] + 1;
        rt[j] = n;
        dq.emplace_back(j);
      }
    } else {
      int ll = lt[dq.back()], rr = rt[dq.back()], i = rr;
      while (ll <= rr) {
        int mm = (ll + rr) / 2;
        if (w(j, mm) < w(dq.back(), mm)) {
          i = mm;
          rr = mm - 1;
        } else {
          ll = mm + 1;
        }
      }
      rt[dq.back()] = i - 1;
      lt[j] = i;
      rt[j] = n;
      dq.emplace_back(j);
    }
    f[j] = w(dq.front(), j);
    opt[j] = dq.front();
  }
  return {f, opt};
}

int main() {
  int n, m;
  std::cin >> n >> m;
  std::vector<long long> a(n + 1), ps(n + 1);
  for (int i = 1; i <= n; ++i) {
    std::cin >> a[i];
    ps[i] = ps[i - 1] + a[i];
  }
  // Cost function for interval [l,r].
  auto w = [&](int j, int i) -> long long {
    int mm = j + (i - j) / 2;
    return ps[i] + ps[j - 1] - 2 * ps[mm] + (2 * mm - j - i + 1) * a[mm];
  };
  // Calculate h(k) = min_x f(x) - k * g(x).
  // Also record the minimum of optimal number of segments.
  auto calc = [&](long long k) -> std::pair<long long, int> {
    auto res = monotone_decision_opt_dp(
        n, [&](int j, int i) -> long long { return w(j, i) - k; });
    auto val = res.first[n];
    int cnt = 0;
    for (int i = n; i; i = res.second[i] - 1) {
      ++cnt;
    }
    return {val, cnt};
  };
  // WQS binary search.
  // Find the largest k such that g(x) <= m.
  long long val, tar_val;
  int opt_m, tar_k;
  long long ll = -(1LL << 32), rr = 0;
  while (ll <= rr) {
    long long mm = ll + (rr - ll) / 2;
    std::tie(val, opt_m) = calc(mm);
    if (opt_m <= m) {
      tar_val = val;
      tar_k = mm;
      ll = mm + 1;
    } else {
      rr = mm - 1;
    }
  }
  long long res = tar_val + (long long)tar_k * m;
  std::cout << res << std::endl;
  return 0;
}

Dual method

#include <cmath>
#include <deque>
#include <iostream>
#include <type_traits>
#include <utility>
#include <vector>

// Monotone decision DP.
// This solves f(i) = min f(j-1) + w(j,i) s.t. 1 <= j <= i.
// Also records the minimal optimal decision j for each f(i).
template <typename W>
std::pair<std::vector<decltype(std::declval<W>()(0, 0))>, std::vector<int>>
monotone_decision_opt_dp(int n, W ww) {
  using ValueT = decltype(std::declval<W>()(0, 0));
  std::vector<ValueT> f(n + 1);
  std::vector<int> opt(n + 1), lt(n + 1), rt(n + 1);
  std::deque<int> dq;
  auto w = [&](int j, int i) -> ValueT { return ww(j, i) + f[j - 1]; };
  for (int j = 1; j <= n; ++j) {
    if (!dq.empty() && rt[dq.front()] < j) dq.pop_front();
    if (!dq.empty()) lt[dq.front()] = j;
    while (!dq.empty() && w(j, lt[dq.back()]) < w(dq.back(), lt[dq.back()])) {
      dq.pop_back();
    }
    if (dq.empty()) {
      lt[j] = j;
      rt[j] = n;
      dq.emplace_back(j);
    } else if (w(j, rt[dq.back()]) >= w(dq.back(), rt[dq.back()])) {
      if (rt[dq.back()] < n) {
        lt[j] = rt[dq.back()] + 1;
        rt[j] = n;
        dq.emplace_back(j);
      }
    } else {
      int ll = lt[dq.back()], rr = rt[dq.back()], i = rr;
      while (ll <= rr) {
        int mm = (ll + rr) / 2;
        if (w(j, mm) < w(dq.back(), mm)) {
          i = mm;
          rr = mm - 1;
        } else {
          ll = mm + 1;
        }
      }
      rt[dq.back()] = i - 1;
      lt[j] = i;
      rt[j] = n;
      dq.emplace_back(j);
    }
    f[j] = w(dq.front(), j);
    opt[j] = dq.front();
  }
  return {f, opt};
}

// Golden section search on integer domain (unimodal function)
template <typename T, typename F>
typename std::enable_if<
    std::is_integral<T>::value,
    std::pair<T, decltype(std::declval<F>()(std::declval<T>()))>>::type
golden_section_search(T ll, T rr, F func) {
  constexpr long double phi = 0.618033988749894848204586834L;
  T ml = ll + static_cast<T>((rr - ll) * (1 - phi));
  T mr = ll + static_cast<T>((rr - ll) * phi);
  auto fl = func(ml), fr = func(mr);
  while (ml < mr) {
    if (fl > fr) {
      rr = mr;
      mr = ml;
      fr = fl;
      ml = ll + static_cast<T>((rr - ll) * (1 - phi));
      fl = func(ml);
    } else {
      ll = ml;
      ml = mr;
      fl = fr;
      mr = ll + static_cast<T>((rr - ll) * phi);
      fr = func(mr);
    }
  }
  T best_x = ll;
  auto best_val = func(ll);
  for (T i = ll + 1; i <= rr; ++i) {
    auto val = func(i);
    if (val > best_val) {
      best_val = val;
      best_x = i;
    }
  }
  return {best_x, best_val};
}

int main() {
  int n, m;
  std::cin >> n >> m;
  std::vector<long long> a(n + 1), ps(n + 1);
  for (int i = 1; i <= n; ++i) {
    std::cin >> a[i];
    ps[i] = ps[i - 1] + a[i];
  }
  // Cost function for interval [l,r].
  auto w = [&](int j, int i) -> long long {
    int mm = j + (i - j) / 2;
    return ps[i] + ps[j - 1] - 2 * ps[mm] + (2 * mm - j - i + 1) * a[mm];
  };
  // Calculate h(k) = min_x f(x) - k * g(x).
  auto solve = [&](long long k) -> long long {
    return monotone_decision_opt_dp(
               n, [&](int j, int i) -> long long { return w(j, i) - k; })
               .first[n] +
           k * m;
  };
  // Solve the dual problem to find v(m).
  auto res = golden_section_search(-(1LL << 32), 0LL, solve).second;
  std::cout << res << std::endl;
  return 0;
}

Two-Dimensional Constraints

Codeforces 739 E. Gosha is hunting

There are nn Pokémon. The sequences {pi}\{p_i\} and {qi}\{q_i\} give the probabilities of catching Pokémon ii with a Poké Ball and a Great Ball, respectively. For each Pokémon, we may throw a Poké Ball, a Great Ball, both, or neither. We have m1m_1 Poké Balls and m2m_2 Great Balls; distribute and throw them simultaneously to maximize the expected number of Pokémon caught. The outcomes of individual attempts are independent.

More generally, this can be abstracted as follows:

Given three sequences of positive real numbers of length nn, {Ai},{Bi},{Ci}\{A_i\},\{B_i\},\{C_i\}, and suppose that for every i=1,⋯ ,ni=1,\cdots,n, Ci≤Ai+BiC_i\le A_i+B_i holds. Find index sets XX and YY with ∣X∣=m1|X|=m_1 and ∣Y∣=m2|Y|=m_2 that maximize

∑i∈X∖YAi+∑i∈Y∖XBi+∑i∈X∩YCi.\sum_{i\in X\setminus Y}A_i + \sum_{i\in Y\setminus X}B_i + \sum_{i\in X\cap Y}C_i.
Solution

The original problem is the special case of this general problem with

Ai=pi, Bi=qi, Ci=pi+qi−piqiA_i = p_i,~ B_i = q_i,~ C_i = p_i+q_i-p_iq_i

Thus it suffices to solve the general problem.

Let v(m1,m2)v(m_1,m_2) denote the value function of this problem. We need to prove that it is concave in (m1,m2)(m_1,m_2). Consider the following cost-flow model:

  • From the source ss, connect edges to nodes xx and yy, with capacities m1m_1 and m2m_2, respectively, and with cost 00 on both edges;
  • For every i=1,⋯ ,ni=1,\cdots,n, connect node xx and node yy to node ii, with capacity 11 on both edges and costs AiA_i and BiB_i, respectively;
  • For every i=1,⋯ ,ni=1,\cdots,n, connect node ii to the sink tt with two edges, each of capacity 11 and with costs 00 and Ci−Ai−BiC_i-A_i-B_i, respectively.

The answer is the maximum cost of a maximum flow in this cost-flow model. The condition Ci−Ai−Bi≤0C_i-A_i-B_i\le 0 guarantees that when the flow through node ii has amount 11, it will preferentially leave along the edge with cost 00. Writing this cost-flow model as a linear program, m1m_1 and m2m_2 appear respectively in the inequalities constraining the flow on edges (s,x)(s,x) and (s,y)(s,y). Therefore, v(m1,m2)v(m_1,m_2) is indeed a concave function of (m1,m2)(m_1,m_2).

To apply WQS binary search, consider the optimization problem after removing the cardinality constraints. Let k1k_1 and k2k_2 be the extra rewards obtained by placing an index into sets XX and YY, respectively. Without cardinality constraints, the decision for each index is independent, so

h(k1,k2)=∑i=1nmax⁡{0,Ai+k1,Bi+k2,Ci+k1+k2}.h(k_1,k_2) = \sum_{i=1}^n\max\{0,A_i+k_1,B_i+k_2,C_i+k_1+k_2\}.

The answer to the original problem is given by

v(m1,m2)=min⁡k1,k2h(k1,k2)−k1m1−k2m2v(m_1,m_2) = \min_{k_1,k_2} h(k_1,k_2) - k_1m_1 - k_2m_2

The total time complexity is O(nlog⁡2L)O(n\log^2L), where O(log⁡L)O(\log L) is the number of binary-search iterations for a single dimension.

A reference implementation for the Pokémon problem follows:

#include <algorithm>
#include <iomanip>
#include <iostream>
#include <tuple>
#include <type_traits>
#include <vector>

// Golden section search on floating-point domain (unimodal function)
template <typename T, typename F>
typename std::enable_if<
    std::is_floating_point<T>::value,
    std::pair<T, decltype(std::declval<F>()(std::declval<T>()))>>::type
golden_section_search(T ll, T rr, F func, T eps) {
  constexpr long double phi = 0.618033988749894848204586834L;
  T ml = ll + (rr - ll) * (1 - phi);
  T mr = ll + (rr - ll) * phi;
  auto fl = func(ml), fr = func(mr);
  while ((rr - ll) > eps) {
    if (fl > fr) {
      rr = mr;
      mr = ml;
      fr = fl;
      ml = ll + (rr - ll) * (1 - phi);
      fl = func(ml);
    } else {
      ll = ml;
      ml = mr;
      fl = fr;
      mr = ll + (rr - ll) * phi;
      fr = func(mr);
    }
  }
  T mid = (ll + rr) / 2;
  return {mid, func(mid)};
}

int main() {
  int n, m1, m2;
  std::cin >> n >> m1 >> m2;
  std::vector<long double> p(n + 1), q(n + 1);
  for (int i = 1; i <= n; ++i) std::cin >> p[i];
  for (int i = 1; i <= n; ++i) std::cin >> q[i];
  // Calculate h(k1,k2).
  auto solve = [&](long double k1, long double k2) -> long double {
    long double res = 0;
    for (int i = 1; i <= n; ++i) {
      res += std::max(
          {0.0l, p[i] + k1, q[i] + k2, p[i] + q[i] - p[i] * q[i] + k1 + k2});
    }
    return res;
  };
  // Solve the dual problem to find v(m1,m2).
  // Implemented as a minimization problem by adding negative signs.
  auto res = -golden_section_search(
                  -1.0l, 0.0l,
                  [&](long double k1) -> long double {
                    return golden_section_search(
                               -1.0l, 0.0l,
                               [&](long double k2) -> long double {
                                 return -solve(k1, k2) + k2 * m2;
                               },
                               1e-8l)
                               .second +
                           k1 * m1;
                  },
                  1e-8l)
                  .second;
  std::cout << std::fixed << std::setprecision(10) << res << std::endl;
  return 0;
}

More General Constraints

Codeforces 1661 F. Teleporters

There are nn segments with lengths given by {ai}\{a_i\}. They may be cut arbitrarily into segments of integer lengths. Minimize the sum of the squares of all segment lengths, and find the minimum number of cuts needed to make this sum no greater than VV.

Solution

Let aa be the minimum sum of squares obtained by cutting a segment of length mm f(a,m)f(a,m) times. By the arithmetic-mean inequality, for a fixed sum, the smaller the difference between two numbers, the smaller their sum of squares. Thus the more evenly the resulting lengths are distributed, the smaller the total sum of squares. Because lengths must be integers, the most even distribution consists of a mod (m+1)a\bmod (m+1) segments of length ⌈a/(m+1)⌉\lceil a/(m+1)\rceil and m+1−(a mod (m+1))m+1-(a\bmod (m+1)) segments of length ⌊a/(m+1)⌋\lfloor a/(m+1)\rfloor. Hence

f(a,m)=(a mod (m+1))⌈am+1⌉2+(m+1−(a mod (m+1)))⌊am+1⌋2=(a mod (m+1))(⌊am+1⌋+1)2+(m+1−(a mod (m+1)))⌊am+1⌋2.\begin{aligned} f(a,m) &= (a\bmod (m+1))\left\lceil\dfrac{a}{m+1}\right\rceil^2 + (m+1-(a\bmod (m+1)))\left\lfloor\dfrac{a}{m+1}\right\rfloor^2 \\ &= (a\bmod (m+1))\left(\left\lfloor\dfrac{a}{m+1}\right\rfloor+1\right)^2 + (m+1-(a\bmod (m+1)))\left\lfloor\dfrac{a}{m+1}\right\rfloor^2. \end{aligned}

The equality in the second line holds because ⌈a/(m+1)⌉≠⌊a/(m+1)⌋+1\lceil a/(m+1)\rceil \neq \lfloor a/(m+1)\rfloor + 1 if and only if a mod (m+1)=0a\bmod (m+1) = 0.

The function f(a,m)f(a,m) is convex in mm. To see this, extend it to m∈R+m\in\mathbf R_{+}. When ⌊a/(m+1)⌋=q\lfloor a/(m+1)\rfloor = q,

f(a,m)=(a−(m+1)q)(q+1)2+((m+1)(q+1)−a)q2=a(2q+1)−q(q+1)(m+1).\begin{aligned} f(a,m) &= (a-(m+1)q)(q+1)^2 + ((m+1)(q+1)-a)q^2 \\ &= a(2q+1) - q(q+1)(m+1). \end{aligned}

This is a line with slope −q(q+1)-q(q+1). Thus f(a,m)f(a,m) is piecewise linear, with slope increasing as mm increases. Therefore it is convex, and its restriction to the integers is also convex5.

Using f(a,m)f(a,m), the minimum sum of squares obtained by making a total of f(⋅,⋅)f(\cdot,\cdot) cuts can be written as the value function

v(m)=min⁡{mi}∑if(ai,mi) subject to ∑imi=m, mi∈N.v(m) = \min_{\{m_i\}}\sum_i f(a_i,m_i)\text{ subject to }\sum_i m_i=m,~m_i\in\mathbf N.

This is the infimal convolution, so it is also convex. If the problem asked for mm, the method from the previous examples would solve it in v(m)v(m) time. Here, however, we need the smallest O(nlog⁡2L)O(n\log^2L) satisfying v(m)≤Vv(m)\le V. Computing mm with WQS binary search and then binary-searching v(m)v(m) would take mm time. There are two ways to handle this problem.

Method 1: still binary-search the slope O(nlog⁡3L)O(n\log^3L), but use estimates of upper and lower bounds for kk.

In the traditional WQS binary-search method, a given slope v(m)v(m) determines a range of optimal values of kk. Since the corresponding mm are collinear, this is equivalent to determining a range for (m,v(m))(m,v(m)). We can therefore binary-search the slope v(m)v(m) directly. Once kk is known, use the line equation

v(m)=h(k)+kmv(m) = h(k) + km

to compute the minimum kk. The total complexity is mm.

To determine the range of O(nlog⁡2L)O(n\log^2L), first determine the range of v(m)v(m). One approach records the largest optimal solution while computing mm and uses it to obtain a lower bound on h(k)h(k). Another uses v(m)v(m) to obtain an upper bound on h(k)−h(k−1)h(k)-h(k-1), and then a lower bound on mm. The reference implementation uses the second approach, which does not depend on the particular structure of the problem.

Method 2: rewrite the optimization problem so that the value function of the dual problem is the solution to this problem.

This problem can be viewed directly as

m(v)=min⁡{mi}∑imi subject to ∑if(ai,mi)≤V.m(v) = \min_{\{m_i\}} \sum_i m_i \text{ subject to }\sum_i f(a_i,m_i) \le V.

The analysis in this article still applies. We can therefore solve the desired v(m)v(m) using its dual problem:

m(v)=max⁡k∑imin⁡mi(mi−λf(ai,mi))+λV.m(v) = \max_{k} \sum_i\min_{m_i}(m_i - \lambda f(a_i,m_i)) + \lambda V.

The overall complexity remains m(v)m(v).

The reference code is as follows:

Method 1

The code is illustrative only. To pass the original constraints, 128-bit integers are required and the initial binary-search interval must be changed to O(nlog⁡2L)O(n\log^2L).

#include <algorithm>
#include <iostream>
#include <tuple>
#include <type_traits>
#include <vector>

// Golden section search on integer domain (unimodal function)
template <typename T, typename F>
typename std::enable_if<
    std::is_integral<T>::value,
    std::pair<T, decltype(std::declval<F>()(std::declval<T>()))>>::type
golden_section_search(T ll, T rr, F func) {
  constexpr long double phi = 0.618033988749894848204586834L;
  T ml = ll + static_cast<T>((rr - ll) * (1 - phi));
  T mr = ll + static_cast<T>((rr - ll) * phi);
  auto fl = func(ml), fr = func(mr);
  while (ml < mr) {
    if (fl > fr) {
      rr = mr;
      mr = ml;
      fr = fl;
      ml = ll + static_cast<T>((rr - ll) * (1 - phi));
      fl = func(ml);
    } else {
      ll = ml;
      ml = mr;
      fl = fr;
      mr = ll + static_cast<T>((rr - ll) * phi);
      fr = func(mr);
    }
  }
  T best_x = ll;
  auto best_val = func(ll);
  for (T i = ll + 1; i <= rr; ++i) {
    auto val = func(i);
    if (val > best_val) {
      best_val = val;
      best_x = i;
    }
  }
  return {best_x, best_val};
}

int main() {
  int n;
  std::cin >> n;
  std::vector<int> a(n + 1);
  for (int i = 1; i <= n; ++i) std::cin >> a[i];
  for (int i = n; i >= 1; --i) a[i] -= a[i - 1];
  long long v;
  std::cin >> v;
  // Cost of adding M more teleporters to a segment of length LEN.
  auto f = [&](int len, int m) -> long long {
    long long rem = len % (m + 1);
    int q = len / (m + 1);
    return (m + 1 - rem) * q * q + rem * (q + 1) * (q + 1);
  };
  // Calculate h(k) = min_x f(x) - k * g(x).
  auto calc = [&](long long k) -> long long {
    long long res = 0;
    for (int i = 1; i <= n; ++i) {
      res += -golden_section_search(0, a[i], [&](int m) -> long long {
                return -f(a[i], m) + m * k;
              }).second;
    }
    return res;
  };
  // Find the smallest k such that h(k) + k * m <= v.
  long long ll = -(1LL << 30), rr = 0, ti = 0;
  while (ll <= rr) {
    auto mm = ll + (rr - ll) / 2;
    auto fi = calc(mm);
    auto ub = fi - calc(mm + 1);
    if (fi + ub * mm <= v) {
      ti = mm;
      rr = mm - 1;
    } else {
      ll = mm + 1;
    }
  }
  std::cout << (int)((calc(ti) - v - 1 - ti) / (-ti)) << std::endl;
  return 0;
}

Method 2

The code is illustrative only and cannot pass the original constraints because of floating-point precision.

#include <algorithm>
#include <cmath>
#include <iostream>
#include <tuple>
#include <type_traits>
#include <vector>

// Golden section search on integer domain (unimodal function)
template <typename T, typename F>
typename std::enable_if<
    std::is_integral<T>::value,
    std::pair<T, decltype(std::declval<F>()(std::declval<T>()))>>::type
golden_section_search(T ll, T rr, F func) {
  constexpr long double phi = 0.618033988749894848204586834L;
  T ml = ll + static_cast<T>((rr - ll) * (1 - phi));
  T mr = ll + static_cast<T>((rr - ll) * phi);
  auto fl = func(ml), fr = func(mr);
  while (ml < mr) {
    if (fl > fr) {
      rr = mr;
      mr = ml;
      fr = fl;
      ml = ll + static_cast<T>((rr - ll) * (1 - phi));
      fl = func(ml);
    } else {
      ll = ml;
      ml = mr;
      fl = fr;
      mr = ll + static_cast<T>((rr - ll) * phi);
      fr = func(mr);
    }
  }
  T best_x = ll;
  auto best_val = func(ll);
  for (T i = ll + 1; i <= rr; ++i) {
    auto val = func(i);
    if (val > best_val) {
      best_val = val;
      best_x = i;
    }
  }
  return {best_x, best_val};
}

// Golden section search on floating-point domain (unimodal function)
template <typename T, typename F>
typename std::enable_if<
    std::is_floating_point<T>::value,
    std::pair<T, decltype(std::declval<F>()(std::declval<T>()))>>::type
golden_section_search(T ll, T rr, F func, T eps) {
  constexpr long double phi = 0.618033988749894848204586834L;
  T ml = ll + (rr - ll) * (1 - phi);
  T mr = ll + (rr - ll) * phi;
  auto fl = func(ml), fr = func(mr);
  while ((rr - ll) > eps) {
    if (fl > fr) {
      rr = mr;
      mr = ml;
      fr = fl;
      ml = ll + (rr - ll) * (1 - phi);
      fl = func(ml);
    } else {
      ll = ml;
      ml = mr;
      fl = fr;
      mr = ll + (rr - ll) * phi;
      fr = func(mr);
    }
  }
  T mid = (ll + rr) / 2;
  return {mid, func(mid)};
}

int main() {
  int n;
  std::cin >> n;
  std::vector<int> a(n + 1);
  for (int i = 1; i <= n; ++i) std::cin >> a[i];
  for (int i = n; i >= 1; --i) a[i] -= a[i - 1];
  long long v;
  std::cin >> v;
  // Cost of adding M more teleporters to a segment of length LEN.
  auto f = [&](int len, int m) -> long long {
    long long rem = len % (m + 1);
    int q = len / (m + 1);
    return (m + 1 - rem) * q * q + rem * (q + 1) * (q + 1);
  };
  // Calculate h(k) = min_x f(x) - k * g(x).
  auto calc = [&](long double k) -> long double {
    long double res = 0;
    for (int i = 1; i <= n; ++i) {
      res += -golden_section_search(0, a[i], [&](int m) -> long double {
                return -m + k * f(a[i], m);
              }).second;
    }
    return res;
  };
  // Solve the dual problem.
  auto res =
      golden_section_search(
          -1.0l, 0.0l,
          [&](long double k) -> long double { return calc(k) + k * v; }, 1e-12l)
          .second;
  std::cout << (int)ceill(res) << std::endl;
  return 0;
}

[0,1060][0,10^{60}]

Exercises

Finally, here are some problems that can be solved with WQS binary search for practice:

References and Notes

Footnotes

  1. In practice, yy may take only finitely many lattice points in Rd\mathbf R^d. The condition actually needed is that the solution v(y)v(y) of the original problem can be extended to a convex function Rd\mathbf R^d on v~:Rd→R∪{±∞}\tilde v:\mathbf R^d\rightarrow \mathbf R\cup\{\pm\infty\}; in other words, v(y)v(y) is convex-extensible. For convenience, the main text still uses v(y)v(y) for the extension. Geometrically, this means that all points {(y,v(y))}\{(y,v(y))\} lie on the lower convex hull of their convex hull. In one dimension, this condition has a simple algebraic characterization which is easy to characterize. For higher dimensions, however, the situation is somewhat more complicated; this lecture note provides some simple sufficient conditions. ↩

  2. The theorem's conditions may appear stronger than convexity, but in the cases arising in programming contests—especially when XX is finite—it is enough to require convexity. The extension vv of a proper convex function v~\tilde v on a discrete set is necessarily lower semicontinuous and convex: the convex hull of finitely many points is closed, and a lower semicontinuous convex function is exactly one whose epigraph is closed and convex. The word “proper” is also automatic as long as v(y)v(y) is finite at at least one point and convex. ↩

  3. There are two common ways: the subtour-elimination formulation and the cut-based formulation. Only the first formulation guarantees equivalence between the resulting linear program and the original problem. ↩

  4. This lemma also holds for general matroids. It is called the symmetric base-exchange property; see the relevant Wikipedia page. Thus the convexity conclusion for this problem extends to general matroids. ↩

  5. Of course, the convex hull of f(a,m)f(a,m) is not the same as the function obtained by restricting it to integer points, because f(a,m)f(a,m) may have extrema at noninteger positions. Thus the real-domain function f(a,m)f(a,m) cannot be used directly in the optimization problem here. ↩

This content comes from OI Wiki: WQS Binary Search. See the original for authors and revision history. AIOJ has adapted the formatting, in-site links, and resource paths.

Content is shared under CC BY-SA 4.0 and SATA license; additional terms are governed by the original notice. Thanks to the OI Wiki authors and contributors; per the SATA terms, please visit the OI Wiki repository and star it to show your support.