Appearance
The Aliens Trick (WQS Binary Search)
Quick Revisit
- USE WHEN: DP with "exactly
items" constraint where is convex — trade the dimension for binary search on a per-item penalty - INVARIANT:
is the Lagrangian dual; answer is where - TIME: O(n log C · T) where T = inner DP cost, C = cost range | SPACE: same as inner DP
- CLASSIC BUG: Not handling the flat region of
where multiple values share the same slope (tie-breaking in cnt) - PRACTICE: 04-ladder-dp
You need to choose exactly
Difficulty: [Advanced]Prerequisites: DP -- 1D Introduction, Binary SearchCF Rating Range: 2200-2600+
Origin Story
IOI 2016, problem "Aliens." Contestants needed to cover certain cells in a grid using exactly
The trick has been independently discovered many times: Lagrangian relaxation in optimization, WQS binary search (named after the Chinese competitive programmer 王钦石), and lambda optimization in the ICPC community.
The Mathematical Setup
You want to compute:
Key assumption:
The trick: instead of enforcing "exactly
This is the Lagrangian dual. For each
f(k): cost vs number of items (convex curve)
cost
| *
| * *
|* * The tangent line with slope -lambda
| * touches the curve at exactly one k.
| * *
| *
+-----------> k
g(lambda) = min_k [ f(k) + lambda*k ]
= value where tangent with slope -lambda touches fThe answer is
Algorithm Skeleton
1. Binary search on lambda in [lo, hi]:
lo = 0, hi = max possible cost per item
2. For each lambda, solve the UNCONSTRAINED DP:
dp[i] = min cost of covering positions 1..i
where each "item used" adds penalty lambda
Track cnt[i] = number of items used in optimal solution for dp[i]
3. If cnt[n] <= k: decrease lambda (items too expensive, using too few)
If cnt[n] > k: increase lambda
4. When binary search converges: answer = dp[n] - lambda * kImplementation Skeleton
cpp
#include <bits/stdc++.h>
using namespace std;
int n, K;
// problem-specific data
pair<long long, int> solveDP(long long lam) {
// dp[i] = {min cost, item count} for positions 0..i
// with penalty lam per item
vector<long long> dp(n + 1, 1e18);
vector<int> cnt(n + 1, 0);
dp[0] = 0; cnt[0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
long long cost = dp[j] + C(j + 1, i) + lam; // +lam = one more item
if (cost < dp[i] || (cost == dp[i] && cnt[j] + 1 < cnt[i])) {
dp[i] = cost;
cnt[i] = cnt[j] + 1;
}
}
}
return {dp[n], cnt[n]};
}
int main() {
// read n, K, problem data
long long lo = 0, hi = 1e18;
long long ans = 0;
// Binary search on lambda (integer or real)
while (lo <= hi) {
long long mid = (lo + hi) / 2;
auto [cost, items] = solveDP(mid);
if (items <= K) {
ans = cost - mid * K;
hi = mid - 1;
} else {
lo = mid + 1;
}
}
cout << ans << "\n";
}The inner DP should be accelerated -- typically with CHT, D&C optimization, or Li Chao tree -- to bring it from
Recognizing the Pattern
Reach for the Aliens trick when you see:
- "Exactly
items/groups/segments" in the constraint. - The cost function is convex in
-- adding more items has diminishing (or increasing, for max problems) returns. - Removing the "exactly
" constraint makes the DP simpler -- drops one dimension.
Classic signals: "partition into exactly
Convexity check: if you can argue that
Pitfalls
- Non-convex
? The trick simply doesn't work. If you can't prove convexity, don't use it -- you'll get WA. - Integer vs. real binary search. If costs are integers, binary search on integer
and handle ties in item count carefully. If costs are real, use a fixed number of iterations (60-80) with floating point. - Tie-breaking in the inner DP. When two transitions give the same cost, prefer the one using fewer (or more) items. This ensures
is monotone. - Large penalty range. Set the binary search bounds wide enough.
can range from to . - Off-by-one in counting items. Make sure each "item" in the DP correctly increments the counter.
Practice Problems
| # | Problem | Difficulty | Notes |
|---|---|---|---|
| 1 | IOI 2016 -- Aliens | IOI Gold | The original problem; DP + CHT + Aliens trick |
| 2 | CF 739E -- Gosha is Hunting | 2800 | Two types of balls, exactly |
| 3 | CF 1279F -- New Year and Handle Change | 2600 | Exactly |
| 4 | APIO 2014 -- Sequence | -- | Partition into |
Further Reading
- DP -- Convex Hull Trick -- often used to accelerate the inner DP
- DP -- D&C Optimization -- another common inner DP accelerator
- Binary Search -- the outer loop
- CF blog: WQS Binary Search -- community explanation with examples