Given two integers and , construct a sequence with maximizing the total number of -bits across all the (the total popcount).
Output that maximum.
- Binary.
- Make all 's?
- ?
- Greedy.
IdeaGreedily use all-ones numbers, packed as evenly as possibleAC
First thoughts: if some has one-bits, you clearly want it to be as small as possible while keeping those bits.
Any with one-bits can be replaced by the smallest number with one-bits — the "all ones" number . The popcount is unchanged and the sum only shrinks, which can never hurt: it leaves more room under to squeeze in more bits elsewhere. We use this implicitly throughout.
As a reminder, the numbers are exactly the ones that look like a run of 's in binary:
b = 1: 2^1 - 1 = 1 = 1
b = 2: 2^2 - 1 = 3 = 11
b = 3: 2^3 - 1 = 7 = 111
b = 4: 2^4 - 1 = 15 = 1111I didn't do this explicitly while solving, but the observation above really flips the problem: for a given popcount, how small can the sum be? If it fits under , that popcount is achievable.
For example, and both contribute to the popcount, but is smaller — and the slack might buy another bit (with : taking spends the whole budget for bits, while gets bits with budget to spare).
In an optimal solution, we can assume every is of the form for some .
This problem is almost easier to see by example than to explain rigorously. Take : has sum and bits; doesn't use both slots; but has sum and bits — the best. Playing with a few of these suggests the winning shape: all-ones numbers, packed as evenly as possible.
Should you ever hold a and a when you could hold and instead? It seems you never really want one number to be much bigger than another. We can prove this with an exchange argument:
Writing each , we can assume for all : the bit-counts are as balanced as possible.
Suppose . Move one bit from the big number to the small one: , . The popcount is unchanged, and the sum changes (the 's cancel) by
since . So balancing strictly decreases the sum at equal popcount — always a good trade.
This is the main insight. All the differ by at most one, so there is some level where every item has bits except for a few upgraded to . Greedily, should be the largest level at which items even fit:
Let be the largest with — equivalently . In an optimal solution every item has bits, except some that have .
How many items get the extra bit?
Greedy again: give every item bits, then upgrade items one at a time while the budget lasts. Upgrading one item from to increases the sum by exactly . So with still unspent, the number of upgrades is . (This is automatically less than — if all items could be upgraded, would contradict the maximality of .)
The rest is writing the formulas out carefully.
# largest b with (2^b - 1) * k <= n — increment rather than take logs
b = 0
while (2**(b+1) - 1) * k <= n:
b += 1
leftover = n - k * (2**b - 1)
upgrades = leftover // (2**b) # each extra bit costs 2^b
print(k * b + upgrades)Implementation notes: no logarithms needed — incrementing is simpler and avoids floating point. Use 64-bit integers for the products.
The first greedy ideas here are relatively obvious and obviously correct — the work was checking the mechanics, not finding alternatives.
Review
The answer is , where is the largest with . The path:
- Use all-ones numbers. Any value can be replaced by with the same popcount and a smaller sum — equivalently, flip the problem: minimize the sum for a given popcount. [1][2]
- Balance the bit-counts. An exchange argument (move a bit from a big number to a small one) shows all can differ by at most one. [3]
- Pick the highest level that fits: = largest with ; everyone gets bits. [4]
- Spend the leftover on upgrades at apiece — maximality of guarantees fewer than of them.
References
Problem-solving techniques used:
Easy enough that the first greedy ideas are obviously correct — don't spend time inventing alternatives, just run with it.
Given a number of bits, minimize their sum — a flip of the problem (arguably also a way of working backwards). I never framed it explicitly; I just kept trying to shrink the sum, and that assumption drove the next few insights.
Playing with a few small cases leads to the same insights.
The ending required a bit of math — powers of two, potentially logarithms, edge cases. Not hard, but it needed careful bookkeeping.
Topics:
- Math
- Binary / powers of 2
- Greedy
- Constructive algorithms / proof by construction