PREFIX SUM › PREFIX SUM
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →If the final value is t, element i must move |nums[i] - t| steps, and each step costs cost[i]. Therefore its contribution is cost[i] * |nums[i] - t|. The whole problem is choosing one target value that balances the cost of moving values from both sides toward it.
Between two consecutive values in sorted order, no input value is crossed. Moving the target through that gap changes the total cost at a constant rate: values on the left become more expensive to move, while values on the right become cheaper. The slope can change only when the target reaches an existing value, so an optimal target can be chosen from nums.
Sorting exposes those gaps. For a candidate v[i], the elements before i contribute the distance from their values up to v[i], and the elements after i contribute the distance down to v[i]. Prefix and suffix arrays store those two totals, allowing every candidate cost to be evaluated without scanning the array again.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n log n) | Sorting n value-cost pairs takes O(n log n). The two scans and the final candidate scan each visit every position once, so together they add O(n) and do not change the bound. |
| Space | O(n) extra | The sorted pair list plus pref and suff contain O(n) working values; the returned number is a scalar and no output storage is being excluded here. The bound stays O(n) for every input shape, including all equal values or all distinct values. |
#include <algorithm>
#include <climits>
#include <utility>
#include <vector>
using namespace std;
class Solution {
public:
long long minCost(vector<int>& nums, vector<int>& cost) {
int n = nums.size();
vector<pair<int, int>> v(n);
for (int i = 0; i < n; ++i) {
v[i] = {nums[i], cost[i]};
}
sort(v.begin(), v.end());
vector<long long> pref(n), suff(n);
long long sumCost = 0;
for (int i = 0; i < n; ++i) {
long long previousValue = (i > 0 ? v[i - 1].first : 0);
pref[i] = (i > 0 ? pref[i - 1] : 0)
+ sumCost * (v[i].first - previousValue);
sumCost += v[i].second;
}
sumCost = 0;
for (int i = n - 1; i >= 0; --i) {
long long nextValue = (i < n - 1 ? v[i + 1].first : 0);
suff[i] = (i < n - 1 ? suff[i + 1] : 0)
+ sumCost * (nextValue - v[i].first);
sumCost += v[i].second;
}
long long answer = LLONG_MAX;
for (int i = 0; i < n; ++i) {
answer = min(answer, pref[i] + suff[i]);
}
return answer;
}
};The important detail in the prefix scan is that sumCost contains only positions strictly before i when pref[i] is computed. The gap from the previous value to v[i] is therefore charged exactly to those earlier positions. The suffix scan mirrors this: sumCost contains only positions strictly after i, so the gap is charged to the elements that must move down toward v[i].
There is another way to choose the target. The total cost is a weighted absolute-distance function, so an optimal target is a weighted median: after sorting by value, choose the first value whose cumulative cost is at least half of the total cost. The prefix and suffix weights explain why: before that point, moving right does not increase the cost, and after it, moving right cannot improve the cost.
#include <algorithm>
#include <vector>
using namespace std;
class Solution {
public:
long long minCost(vector<int>& nums, vector<int>& cost) {
int n = nums.size();
vector<pair<int, int>> v(n);
long long totalCost = 0;
for (int i = 0; i < n; ++i) {
v[i] = {nums[i], cost[i]};
totalCost += cost[i];
}
sort(v.begin(), v.end());
long long seenCost = 0;
long long target = v[0].first;
for (const auto& [value, weight] : v) {
seenCost += weight;
if (seenCost * 2 >= totalCost) {
target = value;
break;
}
}
long long answer = 0;
for (const auto& [value, weight] : v) {
long long distance = static_cast<long long>(value) - target;
if (distance < 0) distance = -distance;
answer += distance * weight;
}
return answer;
}
};This is not faster asymptotically: sorting still costs O(n log n), and evaluating the chosen target costs O(n). It is a different arrangement that buys a simpler target-selection proof and avoids the two pref and suff arrays. The sorted pair vector is still O(n) working space, while the additional storage after sorting is O(1).