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 items from items while optimizing a complicated objective. If the optimal objective value after selecting items from the first items is , the answer is . The transition is usually two-dimensional. Directly implementing it takes 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 to every selected item (the “weight” in weighted binary search) and solve the unconstrained problem again. Different values of produce different optimal cardinalities, and the optimal cardinality changes monotonically with . Thus binary search can find the for which the optimal cardinality is exactly . If the optimal value of the penalized problem is , removing the value lost to the added penalties gives the original answer . If one penalized problem takes time, the total complexity becomes , where is the number of binary-search iterations for .
This is the basic idea of WQS binary search. It works only when is convex in ; otherwise, there may be no penalty whose optimal cardinality is exactly . This is why this DP optimization is often called convex optimization DP or convex total monotonicity DP.
The Traditional Method
Let be a nonempty (finite) decision space, let be the objective function, and let be a function imposing constraints. The problem can be viewed as evaluating the value function of the following optimization problem:
For the cardinality-constrained example above, can be viewed as the family of subsets of all items, as one subset, as its value, and as the number of elements in subset . Of course, 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 usually has no useful structure. We can instead consider
The traditional method mainly handles , when there is only one constraint. The figure below shows one possible point set in this case.
The red and blue points are the projections of all possible choices in onto the plane , forming . The original problem asks for the smallest vertical coordinate among the points whose horizontal coordinate is , namely . As changes, all points form the set of red points in the figure.
To obtain the vertical coordinate of , consider a line of slope tangent to . When the slope is appropriate, the line through has the smallest intercept among all lines of slope passing through points of . Let this minimum be
Since also lies on this line, the solution of the original problem is
Suppose is easy to compute for every reasonable . This is often true in contests because the original constraint has been removed. The two most important questions are then:
- Does there exist a slope whose minimum-intercept line passes through ?
- If so, how can we find such a slope ?
The first question is relatively easy. As the slope 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, is a convex function.
The second question is subtler. Since the desired point is known to have horizontal coordinate , a natural idea is to compute at the current optimal solution while computing . In the preceding example, when solving the penalized problem, record the number of selected items in an optimal solution. Compare with the desired , and adjust the next interval accordingly. This is the most traditional WQS binary-search method.
In summary, the traditional WQS binary-search process is:
- Initially, choose a reasonable interval for .
- Choose a in the current interval.
- Solve and record at an optimal solution , obtaining .
- If , the optimal value is ; terminate.
- Otherwise, adjust the interval for according to the relation between and , 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 are collinear, the basic process may fail to compare and correctly. Suppose three collinear red points have horizontal coordinates and their common line has slope . To compute correctly, the last problem computed when the algorithm terminates must be , because is the only slope of a minimum-intercept line that can pass through . However, while computing , the recorded value may be any of . If the recorded is not , the algorithm continues incorrectly and adjusts the interval away from , eventually producing a wrong answer.
One way to handle collinearity is always to make as large as possible (or as small as possible) when recording the optimal solution . At the same time, change the binary-search termination condition from finding for some to finding the smallest (or largest) satisfying (or ). In the example above, this means that computing returns equal to , ensuring that the last computed problem is . Note that the final output is , not , because the recorded need not equal the actual constraint .
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 is accidentally discarded, the fractional part can move the search back toward it, ultimately approaching the correct answer . For example, if the recorded is , smaller than the desired , while computing , the algorithm considers , where is the right endpoint of the current interval. For integers, this should actually be , which rules out approaching later. With real binary search, the interval remains ; for in this interval, computing always records a value at least , strictly greater than . The right half is therefore repeatedly discarded as the algorithm proceeds, and the final range of is guaranteed to lie near . 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 .
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 is difficult to record, nor higher-dimensional cases with multiple coplanar points. We now examine the properties of the optimization problem and give a more general method.
The Dual Method
This section presents a WQS binary-search implementation that only requires, for every , efficient computation of
the value, together with convexity of the original value function over 1. In one sentence, we prove that the value function of the original problem equals the optimal value of its dual problem,
and that the dual objective is concave in , hence unimodal. It can therefore be solved efficiently by ternary search or golden-section search, with complexity still . This completely resolves the problem of recording in the traditional method and allows WQS binary search to be applied in higher dimensions.
This section also shows that the range of can be obtained from without recording it separately while solving . For example, when and the problem involves only integers, the range of is exactly
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 . The Lagrangian is
If any component of is nonzero, the corresponding component of can tend to positive or negative infinity, so
Thus the original problem can be written as
Exchanging the two optimization operations gives the dual problem:
We will show that under the condition that is convex in , strong duality holds: .
Convex Conjugate
To establish strong duality, we need the notion of a convex conjugate.
Convex Conjugate
For a function , its convex conjugate, also called the Legendre–Fenchel transformation, is
Viewed as a function of , is the supremum of a family of linear functions, so it is necessarily convex on .
“Slope Vector” and “Intercept” of a Hyperplane
Every hyperplane in the vector space discussed here has the form
Thus, hyperplanes parallel to the -axis are not considered. For convenience, we informally call the “slope vector” and the “intercept” of the hyperplane. Writing the equation in standard form gives
A normal vector is . Thus, the so-called slope vector consists of the first components of the normal vector after normalizing its last component to .
Geometrically, the convex conjugate of describes hyperplanes with slope vector that intersect the epigraph of the function
of . The minimum intercept among these hyperplanes is . In other words, always lies above the hyperplane , and the hyperplane is tangent at , possibly with other tangent points. Such a hyperplane is a supporting hyperplane of at . 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 over is equivalent to minimizing over :
Therefore,
Thus is concave in , and
The value function of the dual problem is therefore the double convex conjugate of the original value function , also called the biconjugate.
The problem becomes: which functions equal their own biconjugate? The following theorem answers this.
Theorem (Fenchel–Moreau)
For , if and only if one of the following holds:
- is a proper convex function and lower semicontinuous;
- ; or
- .
Proof
A function is proper if and only if it never takes the value and is not identically .
For improper functions, one can verify that and are convex conjugates of one another. Apart from these, if takes the value at any point, then necessarily . Thus these are the only improper functions satisfying . 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 is the convex conjugate of , it is the supremum of linear functions. The epigraph of is therefore an intersection of closed convex sets and is itself closed and convex. Hence every proper function satisfying 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, . By the definition of the convex conjugate, for all ,
Therefore, for all ,
Taking the supremum over on the right gives .
Second, use the hyperplane separation theorem to show . Suppose otherwise: for some , . Since the epigraph is closed and convex and the singleton is compact and convex, the separation theorem gives and such that for every and every ,
Since can be arbitrarily large, . There are two cases.
First let . Divide through by , and set and :
For every , set to obtain
Taking the supremum over on the right gives
Hence
This contradiction rules out .
Finally let . We show that a perturbation converts this into the case. Choose any . By the definition of the convex conjugate, for every and ,
Therefore, for every ,
Since , for sufficiently small we also have
Set , , and . Then
This returns to the previous case and again gives a contradiction.
Therefore no satisfies , so always. Combining the two steps gives .
Therefore, strong duality holds if and only if is convex in 2.
Subgradients
The previous section showed that the value function of the penalized problem is the negative of the convex conjugate of the original value function . 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 and , if satisfies
for every , then is a subgradient of at . The set of all subgradients of at is its subdifferential, denoted .
Geometrically, the subdifferential of a convex function at is the set of slope vectors of all its supporting hyperplanes there. In one dimension,
where and are the left and right derivatives of at . For the extension of a convex function on the integers, its left and right derivatives at the integer point are the first differences on the two sides:
Clearly, a convex function is differentiable at if and only if its subdifferential is a singleton.
Because the convex conjugate maps slope vectors of supporting hyperplanes to their intercepts, it can determine whether a slope vector is a subgradient of at a given point .
Theorem (Convex Conjugates and Subgradients)
For a proper convex function and any ,
If is also lower semicontinuous, these two conditions are also equivalent to .
Proof
By the definition of a subgradient, if and only if
This is equivalent to
Equivalently,
By the definition of the convex conjugate, we always have
Thus the previous inequality is actually an equality, equivalently
This proves the first part.
If is a lower-semicontinuous proper convex function, the Fenchel–Moreau theorem gives . Thus the two conditions are equivalent to
Applying the first part again, they are equivalent to .
This result says that if , then the subdifferential of the convex conjugate at is exactly the set of -components of the intersections between the supporting hyperplanes with slope vector and the epigraph .
Corollary
For a lower-semicontinuous proper convex function and any ,
Proof
We prove the second equality; the first is similar.
By the definition of the convex conjugate,
so
if and only if , and this equality holds if and only if . Thus the two sets are equal.
In the setting of this article, this result says that when solving
the values of the constraint function over the optimal decision set are exactly . When and the problem involves only integers, this set is the interval
For consecutive integer , 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 tree pits, and trees must be planted. Trees cannot be planted in adjacent pits. Given a length- sequence 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 trees.
In short, this is the problem of finding a maximum-weight independent set of size on a path of length .
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
The objective is convex in for every , while the feasible-set mapping is set-valued on and has convex values for every . These conditions make this a convex optimization problem for every parameter .
Theorem
Suppose the parametric convex optimization problem above satisfies:
- The objective is convex in ;
- The graph of the feasible-set mapping is convex.
If for every , then the value function is a proper convex function of .
Proof
For any and , we need to prove
If or , the right-hand side is and the inequality is immediate. Otherwise, and are both finite. For every and , there exists such that . Convexity of the graph of gives
Thus is feasible for the optimization problem with parameter . By optimality and convexity of the objective,
Since is arbitrary, let to obtain
Therefore the value function is convex.
The most common convex optimization problems in algorithm contests are linear programs.
Corollary
Let , , , , and . Consider the parametric linear program
Then the value function is convex in .
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 is a convex function of the flow .
Proof
Let be a directed graph. Let edge have capacity and per-unit cost , with source and sink . Let the decision variables be , where is the flow on edge . The min-cost flow can be written as the linear program
Thus the minimum cost is convex in the parameter .
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 , add an edge to vertex with capacity and cost ;
- From vertex , add an edge of capacity and cost to every odd vertex ;
- From every even vertex , add an edge of capacity and cost to sink ;
- For each , add an edge of capacity and cost from the odd vertex among and 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 appears in the inequality representing the flow constraint on edge . The corollary shows that the maximum cost is concave in the flow .
Using this cost-flow model, the problem can be solved in 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 is convex in the parameter . Specifically, regard as the state at , and view the transition for as a recurrence for . Convexity of every 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 be the maximum benefit when trees are planted in the first pits. Consider
View this transition as a recurrence for . Because the maximum involves two different functions, it cannot be expressed as a supremal convolution. Nevertheless, induction can still prove that is concave.
Specifically, prove inductively that:
- is decreasing in ;
- is increasing in .
The induction base is trivial. Assume these statements hold for all and earlier indices; now prove them for . Direct verification is enough.
First, by the induction hypothesis,
is decreasing in . Therefore,
is decreasing in , and
is increasing in . This completes the induction.
Consequently,
is decreasing in . Thus is concave in , and the value function is concave in .
One by-product of this proof is that for every there is a such that
This means that the sequence can be maintained directly with a balanced tree in time. The benefit is that arbitrary tree-spacing conditions can be handled and all values of 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 satisfies the quadrangle inequality. The most direct method is to compute its mixed second difference:
The function satisfies the quadrangle inequality if and only if 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 before the tree-benefit sequence. The tree-planting problem is then equivalent to partitioning into segments, where the benefit function of each segment is
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
Substitute the benefit function and set
The desired inequality becomes
The larger of and on the left is , while the smaller is always at least , 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 , and the interval-partition algorithm solves the problem in or 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 and , exchange some elements to construct a feasible solution for parameter with value at most , and use optimality of 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 on the family of subsets of a finite set , if it satisfies either of the following equivalent properties:
- (Crossing less than containment) For all subsets , ;
- (Increasing marginal cost) For all and , ;
then is supermodular. However, in an optimization problem with a supermodular objective, the value function
need not be convex in . The reason is that optimal solutions for subset sizes and generally cannot be used to construct a feasible subset of size 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 and trees be and , respectively. A value of means a tree is planted in the pit, and means no tree is planted. Define by
This sequence marks the differences between the two planting schemes. A value of means that the pit either has a tree in both schemes or has no tree in either; and mean that the pit has a tree only in or only in , respectively. Since no scheme plants trees in adjacent pits:
- In every consecutive nonzero segment, the values of alternate between ;
- The values immediately outside a maximal consecutive nonzero segment mean that neither scheme plants a tree there.
If the sum of over a maximal consecutive nonzero segment is , then scheme plants one more tree than scheme in that segment, so we can exchange the planting positions of the two schemes within the segment. This produces two feasible schemes, each planting trees. The total planting positions and count across the two schemes are unchanged, only redistributed, so their total benefit remains . These two -tree schemes need not be optimal, so each has benefit at most . Hence
which means is concave in .
It remains to show that a maximal consecutive nonzero segment with sum exists. Since it is a sum of alternating , the sum of each such segment is or . The sum of all maximal consecutive nonzero segments is , so at least two segments have sum exactly . 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 tree pits, and at most trees may be planted. Trees cannot be planted in adjacent pits. Given a length- sequence representing the benefit of planting in each pit, where benefits may be positive or negative, find the maximum possible total benefit of planting these trees.
Solution
Unlike the tree-planting problem discussed above, this problem asks for at most trees rather than exactly . Let still denote the value function of the earlier problem. The answer here is . Since is concave, hence unimodal, the answer keeps only the part of 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 rather than . After WQS binary search removes the cardinality constraint, the problem becomes maximum-weight independent set on a path, with the original profits replaced by . This is a classic dynamic programming problem. Let be the maximum profit of the subproblem on the first pits when the state means planting and the state means leaving the -th pit empty. The transition is
The initial conditions are and , and the final answer is . One computation takes time, so the total complexity is , where .
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 white edges.
Solution
First, an exchange argument proves that 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 and be two spanning trees of the undirected connected graph . For any , there exists at least one edge such that both and are spanning trees of .
Proof
Let , and let be the unique path in tree connecting and . Since is the unique cycle in , deleting any edge on , say , makes a spanning tree. Meanwhile, is a forest with two connected components, whose vertex sets are and . Therefore, choosing an edge such that the edge connects and guarantees that is a spanning tree. Such an edge always exists because and belong to and , respectively, and connects and . Moreover, , because in , and are disconnected. This completes the proof.
Let and be minimum spanning trees with and white edges, respectively. Let be a white edge in . Applying the lemma shows that there exists an edge such that and are both spanning trees. Since only one pair of edges is exchanged, the sum of the edge weights of and remains . We now consider two cases:
- If is a black edge, then both and have white edges. The edge-weight sum of each is at least . This proves , and hence is convex as a function of .
- If is a white edge, then and have and white edges, respectively, so their edge-weight sums are at least and , respectively. However, we already know that their sums equal . This implies that the edge-weight sum of is exactly . Comparing with shows that and must have equal weights. This contradicts the assumption, so this case cannot occur.
This proves that is convex as a function of .
Once the convexity of has been established, the problem can be solved with WQS binary search. Remove the cardinality constraint, subtract 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 , where and are the numbers of edges and vertices, respectively, and is the inverse Ackermann function. The main part of the complexity, , 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 , where 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 positive integers representing the positions of villages beside a highway, build 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 post offices is therefore equivalent to partitioning the villages into 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 is
It satisfies the quadrangle inequality because its mixed second difference is nonpositive:
Therefore, combining a monotone queue with WQS binary search solves the problem in 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 Pokémon. The sequences and give the probabilities of catching Pokémon 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 Poké Balls and 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 , , and suppose that for every , holds. Find index sets and with and that maximize
Solution
The original problem is the special case of this general problem with
Thus it suffices to solve the general problem.
Let denote the value function of this problem. We need to prove that it is concave in . Consider the following cost-flow model:
- From the source , connect edges to nodes and , with capacities and , respectively, and with cost on both edges;
- For every , connect node and node to node , with capacity on both edges and costs and , respectively;
- For every , connect node to the sink with two edges, each of capacity and with costs and , respectively.
The answer is the maximum cost of a maximum flow in this cost-flow model. The condition guarantees that when the flow through node has amount , it will preferentially leave along the edge with cost . Writing this cost-flow model as a linear program, and appear respectively in the inequalities constraining the flow on edges and . Therefore, is indeed a concave function of .
To apply WQS binary search, consider the optimization problem after removing the cardinality constraints. Let and be the extra rewards obtained by placing an index into sets and , respectively. Without cardinality constraints, the decision for each index is independent, so
The answer to the original problem is given by
The total time complexity is , where 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 segments with lengths given by . 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 .
Solution
Let be the minimum sum of squares obtained by cutting a segment of length 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 segments of length and segments of length . Hence
The equality in the second line holds because if and only if .
The function is convex in . To see this, extend it to . When ,
This is a line with slope . Thus is piecewise linear, with slope increasing as increases. Therefore it is convex, and its restriction to the integers is also convex5.
Using , the minimum sum of squares obtained by making a total of cuts can be written as the value function
This is the infimal convolution, so it is also convex. If the problem asked for , the method from the previous examples would solve it in time. Here, however, we need the smallest satisfying . Computing with WQS binary search and then binary-searching would take time. There are two ways to handle this problem.
Method 1: still binary-search the slope , but use estimates of upper and lower bounds for .
In the traditional WQS binary-search method, a given slope determines a range of optimal values of . Since the corresponding are collinear, this is equivalent to determining a range for . We can therefore binary-search the slope directly. Once is known, use the line equation
to compute the minimum . The total complexity is .
To determine the range of , first determine the range of . One approach records the largest optimal solution while computing and uses it to obtain a lower bound on . Another uses to obtain an upper bound on , and then a lower bound on . 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
The analysis in this article still applies. We can therefore solve the desired using its dual problem:
The overall complexity remains .
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 .
#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;
}
Exercises
Finally, here are some problems that can be solved with WQS binary search for practice:
- Luogu P1484 Planting Trees
- Luogu P1792 [National Training Team] Planting Trees
- Luogu P2619 [National Training Team] Tree I
- Luogu P3620 [APIO/CTSC2007] Data Backup
- Luogu P4072 [SDOI2016] Journey
- Luogu P4383 [Eight Provinces Joint Examination 2018] Link-Cut Tree
- Luogu P4983 Forgetting Passion
- Luogu P5308 [COCI 2018/2019 #4] Akvizna
- Luogu P5633 Minimum-Degree-Constrained Spanning Tree
- Luogu P5896 [IOI 2016] aliens
- Luogu P6246 [IOI 2000] Post Office (Enhanced Enhanced)
- AtCoder Beginner Contest 218 H - Red and Blue Lamps
- AtCoder Beginner Contest 305 Ex - Shojin
- AtCoder Regular Contest 164 E - Segment-Tree Optimization
- Codeforces 125 E. MST Company
- Codeforces 321 E. Ciel and Gondolas
- Codeforces 739 E. Gosha is hunting
- Codeforces 802 O. April Fools' Problem (hard)
- Codeforces 958 E2. Guard Duty (medium)
- Codeforces 1279 F. New Year and Handle Change
- Codeforces 1661 F. Teleporters
- Codeforces 1799 F. Halve or Subtract
- 2019 Summer Petrozavodsk Camp H. Honorable Mention
References and Notes
- Wang Qinshi, A Brief Analysis of a Class of Binary Search Methods
- Theoretical grounds of lambda optimization by adamant - Codeforces blog
- A Rigorous WQS Binary-Search Method by YeahPotato - Luogu Blog
- Study Notes: Detailed WQS Binary Search and Common Misconceptions by ikrvxt - CSDN Blog
- Convex conjugate - Wikipedia
- Fenchel–Moreau theorem - Wikipedia
- Subderivative - Wikipedia
- Boyd, Stephen P., and Lieven Vandenberghe. Convex optimization. Cambridge university press, 2004.
- Papadimitriou, Christos H., and Kenneth Steiglitz. Combinatorial optimization: algorithms and complexity. Courier Corporation, 1998.
- Conforti, Michele, Gérard Cornuéjols, and Giacomo Zambelli. Integer programming. Springer International Publishing, 2014.
- Schrijver, Alexander. Combinatorial optimization: polyhedra and efficiency. Vol. 24, no. 2. Berlin: Springer, 2003.
Footnotes
-
In practice, may take only finitely many lattice points in . The condition actually needed is that the solution of the original problem can be extended to a convex function on ; in other words, is convex-extensible. For convenience, the main text still uses for the extension. Geometrically, this means that all points 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. ↩
-
The theorem's conditions may appear stronger than convexity, but in the cases arising in programming contests—especially when is finite—it is enough to require convexity. The extension of a proper convex function 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 is finite at at least one point and convex. ↩
-
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. ↩
-
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. ↩
-
Of course, the convex hull of is not the same as the function obtained by restricting it to integer points, because may have extrema at noninteger positions. Thus the real-domain function cannot be used directly in the optimization problem here. ↩