Chapter 116: Alien Trick and Parametric Search
Prerequisites
- Binary search on real/integer domains
- Dynamic programming with constraints
- Lagrange multipliers (conceptual understanding)
Interview Frequency: ★★
The alien trick (Lagrangian relaxation with WQS binary search) sits at the hard end of the optimization spectrum and shows up at Google, Amazon, and Meta mainly in senior or research-leaning loops. It is rarely the intended first solution; interviewers use it to see whether you can turn a “choose exactly k items” constraint into an unconstrained problem with a penalty term, then binary-search the penalty.
116.1 What Is the Alien Trick?
The Alien Trick (also called Parametric Search or WQS Binary Search) is an optimization technique that converts a constrained optimization problem into an unconstrained one by introducing a penalty parameter λ (lambda).
Core Idea: If you need to optimize some cost function f(x) subject to using
exactly (or at most) K segments/items, you can instead minimize:
f(x) + λ · g(x)
where g(x) counts the number of segments used. By binary-searching on λ, you find
the value that makes the unconstrained optimum use exactly K segments.
When Does It Apply?
The Alien Trick works when the cost vs. number-of-segments curve is convex. This means:
- Adding one more segment always helps, but with diminishing returns
- The marginal gain of each additional segment is non-increasing
If the curve is convex, the Lagrangian relaxation has a unique optimal point for each λ, and binary search on λ maps cleanly to the number of segments.
116.2 Motivating Example
Problem: Given an array of n positive integers, partition it into exactly K
contiguous subarrays to minimize the maximum subarray sum.
Without the Alien Trick, this requires a DP with states (position, partitions_used)
→ O(n² · K) time.
With the Alien Trick, we reduce it to O(n log C) where C is the range of answers.
Why is this faster? We eliminate one dimension of the DP by absorbing the partition count into the objective function via a penalty.
116.3 Intuition: The Lagrangian Relaxation
In optimization theory, a constrained problem:
minimize f(x) subject to g(x) = K
can be relaxed to:
minimize f(x) + λ · g(x) (no constraint on g(x))
For each value of λ, the unconstrained problem has some optimal solution with a particular value of g(x). As λ increases, we penalize using more segments, so the optimal g(x) decreases. As λ decreases (becomes negative), we reward using more segments, so g(x) increases.
By binary-searching λ, we find the exact value where g(x) = K.
Geometric Picture
Plot the “number of segments” on the x-axis and “cost” on the y-axis. The set of achievable (segments, cost) pairs forms a convex hull. The Alien Trick finds the point on this convex hull where segments = K.
Cost
| .
| .
| . ← Convex hull of achievable points
| .
| .
| .
+---------------- Segments
A line with slope -λ is tangent to this convex hull. Moving λ slides the tangent point along the hull.
116.4 Formal Algorithm
Given:
- A function
solve(λ)that returns(optimal_cost, num_segments)for penalty λ - A target number of segments K
Algorithm:
- Binary search on λ (integer or real-valued)
- For each mid = λ, call
solve(λ)to get(cost, segments) - If
segments >= K: increase λ (penalize segments more) - If
segments < K: decrease λ (encourage more segments) - At the boundary, compute the true answer:
answer = cost - λ * K
Key Insight: The true answer is f(x*) + λ · g(x*) - λ · K, where (x*, g(x*))
is the solution returned by solve(λ).
116.5 Detailed Example: Partition Array to Minimize Maximum Sum
Problem Statement:
Given arr = [1, 3, 2, 4, 1, 5, 2] and K = 3, partition into 3 contiguous subarrays
to minimize the maximum subarray sum.
Step-by-step with Alien Trick:
- The penalty λ represents “cost per partition.”
- For a given λ, solve the unconstrained problem: partition freely to minimize
(max subarray sum) + λ · (number of partitions). - The unconstrained problem can be solved greedily: keep adding elements to the current subarray unless starting a new subarray (cost λ) would be better.
Walkthrough with λ = 2:
arr = [1, 3, 2, 4, 1, 5, 2]
Start new partition at arr[0] = 1, current_sum = 1, partitions = 1
Add arr[1] = 3 → current_sum = 4. Starting new costs λ=2, extending costs 3. Extend.
Add arr[2] = 2 → current_sum = 6. Starting new costs 2, extending costs 2. Either works; extend.
Add arr[3] = 4 → current_sum = 10. Starting new costs 2, extending costs 4. Start new partition.
→ partitions = 2, current_sum = 4
Add arr[4] = 1 → current_sum = 5. Extend.
Add arr[5] = 5 → current_sum = 10. Starting new costs 2, extending costs 5. Start new partition.
→ partitions = 3, current_sum = 5
Add arr[6] = 2 → current_sum = 7. Extend.
Partitions: [1,3,2], [4,1], [5,2] → max sum = 6, partitions = 3
Objective = 6 + 2·3 = 12
Since partitions = 3 = K, we found the answer!
True answer = 12 - 2·3 = 6
Verification: Can we do better than max sum = 6? With 3 partitions on [1,3,2,4,1,5,2]:
- [1,3,2,4], [1,5], [2] → max = 10
- [1,3], [2,4,1], [5,2] → max = 7
- [1,3,2], [4,1], [5,2] → max = 6 ✓
116.6 Implementation in C++
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
struct Result {
long long cost;
int partitions;
};
// Solve unconstrained problem with penalty lambda per partition
Result solve(const std::vector<int>& arr, long long lambda) {
int n = arr.size();
long long currentSum = 0;
long long maxInCurrent = 0;
int partitions = 1; // Always start with at least one partition
for (int x : arr) {
// If extending would make currentSum > lambda + maxInCurrent, start new partition
if (currentSum + x > maxInCurrent + lambda) {
partitions++;
currentSum = x;
maxInCurrent = x;
} else {
currentSum += x;
maxInCurrent = std::max(maxInCurrent, currentSum);
}
}
return {maxInCurrent + lambda * partitions, partitions};
}
long long aliensTrick(const std::vector<int>& arr, int k) {
// Binary search on lambda
long long lo = -1e15, hi = 1e15;
long long answer = 0;
while (lo <= hi) {
long long mid = lo + (hi - lo) / 2;
auto result = solve(arr, mid);
if (result.partitions >= k) {
answer = result.cost - mid * k;
lo = mid + 1;
} else {
hi = mid - 1;
}
}
return answer;
}
int main() {
std::vector<int> arr = {1, 3, 2, 4, 1, 5, 2};
int k = 3;
std::cout << "Min max-subarray with " << k << " partitions: "
<< aliensTrick(arr, k) << "\n";
return 0;
}
116.7 Implementation in Python
def solve(arr, lam):
"""Solve unconstrained problem with penalty `lam` per partition."""
n = len(arr)
current_sum = 0
max_in_current = 0
partitions = 0
for x in arr:
if partitions > 0 and current_sum + x > max_in_current + lam:
# Start new partition
partitions += 1
current_sum = x
max_in_current = x
else:
current_sum += x
max_in_current = max(max_in_current, current_sum)
if partitions == 0:
partitions = 1
return max_in_current + lam * partitions, partitions
def aliens_trick(arr, k):
"""Find minimum max-subarray-sum when partitioning into exactly k parts."""
lo, hi = -10**15, 10**15
answer = 0
while lo <= hi:
mid = (lo + hi) // 2
cost, partitions = solve(arr, mid)
if partitions >= k:
answer = cost - mid * k
lo = mid + 1
else:
hi = mid - 1
return answer
# Example
arr = [1, 3, 2, 4, 1, 5, 2]
k = 3
print(f"Min max-subarray with {k} partitions: {aliens_trick(arr, k)}")
# Output: Min max-subarray with 3 partitions: 6
116.8 Implementation in Java
public class AlienTrick {
static class Result {
long cost;
int partitions;
Result(long cost, int partitions) {
this.cost = cost;
this.partitions = partitions;
}
}
static Result solve(int[] arr, long lambda) {
long currentSum = 0;
long maxInCurrent = 0;
int partitions = 0;
for (int x : arr) {
if (partitions > 0 && currentSum + x > maxInCurrent + lambda) {
partitions++;
currentSum = x;
maxInCurrent = x;
} else {
currentSum += x;
maxInCurrent = Math.max(maxInCurrent, currentSum);
}
}
if (partitions == 0) partitions = 1;
return new Result(maxInCurrent + lambda * partitions, partitions);
}
static long aliensTrick(int[] arr, int k) {
long lo = -1_000_000_000_000_000L;
long hi = 1_000_000_000_000_000L;
long answer = 0;
while (lo <= hi) {
long mid = lo + (hi - lo) / 2;
Result result = solve(arr, mid);
if (result.partitions >= k) {
answer = result.cost - mid * k;
lo = mid + 1;
} else {
hi = mid - 1;
}
}
return answer;
}
public static void main(String[] args) {
int[] arr = {1, 3, 2, 4, 1, 5, 2};
int k = 3;
System.out.println("Min max-subarray with " + k + " partitions: "
+ aliensTrick(arr, k));
}
}
116.9 Complexity Analysis
| Step | Time | Space |
|---|---|---|
solve(λ) | O(n) | O(1) |
| Binary search on λ | O(n · log C) | O(1) |
| Total | O(n · log C) | O(1) |
Where C is the range of possible λ values (typically 10^15 for 64-bit integers).
Comparison with standard DP:
- Standard DP: O(n² · K) time, O(n · K) space
- Alien Trick: O(n · log C) time, O(1) space
The Alien Trick is dramatically faster when n and K are large.
116.10 Common Pitfalls
-
Non-convex cost curve: If the cost vs. segments curve is not convex, the trick fails. The binary search may oscillate or find a wrong answer.
-
Tie-breaking: When multiple solutions have the same cost but different segment counts, you need consistent tie-breaking. The standard approach is to prefer more segments (or fewer) consistently.
-
Off-by-one in λ: The binary search must handle the case where the exact K is achievable but λ lands between two integers.
-
Integer vs. real λ: For problems where the cost function involves integers, integer λ suffices. For real-valued costs, you may need real-valued λ with sufficient precision.
116.11 Practice Problems
- Partition array into K subarrays minimizing max sum (the example above)
- IOI 2016 — Railroad: Minimize cost with exactly K rail segments
- AtCoder DP Contest — X: Partition with penalty per segment
- Codeforces 739E — Gosha is hunting: Two types of “balls” with constraints
- USACO — Balanced Teams: Group students into exactly K teams
116.12 Interview Questions
-
Q: When can you apply the Alien Trick? A: When the cost vs. number-of-segments curve is convex. This means each additional segment provides diminishing returns.
-
Q: What is the time complexity improvement over standard DP? A: From O(n² · K) to O(n · log C), where C is the answer range. The Alien Trick eliminates one DP dimension.
-
Q: How do you handle the case where no valid partition exists? A: Return a sentinel value or check feasibility before applying the trick.
-
Q: Can the Alien Trick be used for maximization problems? A: Yes, with appropriate sign changes. The penalty λ encourages or discourages more segments depending on the optimization direction.
116.13 Dry Run: Full Trace
Input: arr = [7, 2, 5, 10, 8], K = 2
Binary search on λ:
| Iter | λ | Cost | Partitions | Action |
|---|---|---|---|---|
| 1 | 0 | 32 (all in one, max=32) | 1 | partitions < K → decrease λ |
| 2 | -5 | 32 + (-5)·1 = 27 | 1 | still < K → decrease λ |
| 3 | -8 | Split [7,2,5] + [10,8] → max=18, cost=18+(-8)·2=2 | 2 | partitions = K → record answer = 2 - (-8)·2 = 18, increase λ |
| 4 | -6 | Split [7,2,5] + [10,8] → max=18, cost=18+(-6)·2=6 | 2 | partitions = K → answer = 6 - (-6)·2 = 18, increase λ |
| 5 | -4 | [7,2,5]+[10,8] → max=18, cost=18+(-4)·2=10 | 2 | answer = 10 - (-4)·2 = 18, increase λ |
| … | … | … | … | converges |
Final answer: 18 (partition [7,2,5] and [10,8], max sum = max(14, 18) = 18)
116.14 Related Techniques
| Technique | Use Case |
|---|---|
| Alien Trick | Convex optimization with segment count constraint |
| Lagrange Multipliers | Continuous optimization with equality constraints |
| Parametric Search | Binary search on a parameter to find a threshold |
| WQS Binary Search | Same as Alien Trick, named after Wei-Qi Shou |
Summary
| Step | Action |
|---|---|
| 1 | Verify convexity of cost vs. segments curve |
| 2 | Define unconstrained: f(x) + λ · g(x) |
| 3 | Binary search on λ |
| 4 | For each λ, solve unconstrained problem in O(n) |
| 5 | Answer = result_cost - λ · K |
Key Takeaway: The Alien Trick trades a dimension of DP for a binary search, dramatically improving time complexity for constrained partitioning problems.