CF 104452B - Time to reap the harvest
We are given a list of plant heights along a line, and a set of queries. Each query gives a cutting height $L$, and we must compute how much material remains above that cut. For each bush with height $ai$, only the portion above $L$ contributes, and only if it is positive.
CF 104452B - Time to reap the harvest
Rating: -
Tags: -
Solve time: 3m 11s
Verified: yes
Solution
Problem Understanding
We are given a list of plant heights along a line, and a set of queries. Each query gives a cutting height $L$, and we must compute how much material remains above that cut.
For each bush with height $a_i$, only the portion above $L$ contributes, and only if it is positive. So each bush contributes $\max(a_i - L, 0)$. The task is to sum this contribution over all bushes for every query.
So each query is independent: we imagine placing a horizontal cut at height $L$, discarding everything below it, and summing the leftover vertical lengths.
The constraints $N, K \le 10^5$ force us to avoid recomputing the sum from scratch per query. A direct simulation would cost $O(NK)$, which is about $10^{10}$ operations in the worst case, far too slow. This immediately suggests preprocessing the heights so each query can be answered in logarithmic or constant time.
A naive mistake that often appears here is recomputing only over elements $a_i > L$ without preprocessing. For example, if heights are $[10^9, 10^9, \dots]$ and queries are many small values, every query still scans the full array, causing a timeout even though most values behave similarly across queries.
Another subtle pitfall is forgetting that the contribution depends both on how many elements exceed $L$ and by how much they exceed it. Treating it as just a count of elements above $L$ loses magnitude information and produces incorrect answers.
Approaches
The brute-force approach evaluates each query independently. For a given $L$, it iterates through all $a_i$, accumulates $a_i - L$ when $a_i > L$, and outputs the sum. This is correct because it directly follows the definition, but it repeats the same scan $K$ times, resulting in $O(NK)$ time.
The key observation is that for a fixed $L$, only elements greater than $L$ matter, and their contribution can be rewritten in a way that separates counting from summation. If we sort the array and maintain prefix sums, we can quickly compute both how many elements exceed $L$ and their total sum. This reduces each query to a binary search plus arithmetic, allowing efficient evaluation.
We transform the expression:
$$\sum \max(a_i - L, 0) = \sum_{a_i > L} a_i - L \cdot (\text{count of } a_i > L)$$
Once the array is sorted, both terms can be obtained with a single binary search and a prefix sum lookup.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Brute Force | $O(NK)$ | $O(1)$ | Too slow |
| Sorting + Prefix Sum | $O(N \log N + K \log N)$ | $O(N)$ | Accepted |
Algorithm Walkthrough
- Sort the array of heights in non-decreasing order. This allows us to isolate all elements greater than a given query value using binary search.
- Build a prefix sum array where
pref[i]stores the sum of the firstielements in the sorted array. This allows constant-time range sum queries. - For each query value $L$, use binary search to find the first index
possuch thata[pos] > L. Everything fromposto $N-1$ contributes to the answer. - Compute the sum of all elements greater than $L$ as
total = pref[n] - pref[pos]. - Compute how many elements exceed $L$ as
cnt = n - pos. - Subtract the lost base height $L \cdot cnt$ from
totalto obtain the final answer. - Output the result for each query independently.
The key idea is that once the threshold splits the array, the contribution becomes a linear function of the number of remaining elements, so both required statistics come directly from preprocessing.
Why it works
Sorting ensures that all valid contributors form a contiguous suffix of the array. Prefix sums encode cumulative totals, so any suffix sum can be computed in constant time. Since the transformation rewrites the original nonlinear max-expression into a difference of two linear quantities over that suffix, every query reduces to a single interval computation. No information is lost because every term is accounted for exactly once either in the prefix or excluded suffix.
Python Solution
import sys
input = sys.stdin.readline
def solve():
n = int(input())
a = list(map(int, input().split()))
k = int(input())
a.sort()
pref = [0] * (n + 1)
for i in range(n):
pref[i + 1] = pref[i] + a[i]
from bisect import bisect_right
for _ in range(k):
L = int(input())
pos = bisect_right(a, L)
cnt = n - pos
total = pref[n] - pref[pos]
ans = total - L * cnt
print(ans)
if __name__ == "__main__":
solve()
After sorting, we can quickly isolate the suffix of elements that exceed each query threshold. The prefix sum array converts that suffix into a constant-time sum query. The binary search locates the boundary between contributing and non-contributing elements. Each query is then evaluated using a fixed arithmetic expression derived directly from the original definition.
A common implementation mistake is using bisect_left instead of bisect_right, which incorrectly includes elements equal to $L$. Another subtle issue is forgetting that subtraction must be applied after computing the full suffix sum, not per element, otherwise integer operations degrade into unnecessary loops.
Worked Examples
Example 1
Input:
a = [0, 0, 0, 0]
queries = [0, 1, 2]
Sorted array is unchanged, prefix sums are all zero.
| L | pos (first > L) | cnt | suffix sum | answer |
|---|---|---|---|---|
| 0 | 4 | 0 | 0 | 0 |
| 1 | 4 | 0 | 0 | 0 |
| 2 | 4 | 0 | 0 | 0 |
Every value is ≤ L, so no contribution exists. This confirms correctness on fully saturated cuts.
Example 2
Input:
a = [4, 0, 2, 1, 2]
queries = [0, 1, 2, 3, 4]
Sorted: [0, 1, 2, 2, 4], prefix sums: [0,1,3,5,9]
| L | pos | cnt | suffix sum | answer |
|---|---|---|---|---|
| 0 | 1 | 4 | 9 | 9 |
| 1 | 2 | 3 | 8 | 8 - 3 = 5 |
| 2 | 3 | 2 | 6 | 6 - 4 = 2 |
| 3 | 4 | 1 | 4 | 4 - 3 = 1 |
| 4 | 5 | 0 | 0 | 0 |
This trace shows how the answer decreases linearly as the threshold increases, reflecting shrinking suffix size.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | $O(N \log N + K \log N)$ | sorting plus one binary search per query |
| Space | $O(N)$ | prefix sums stored for constant-time range sums |
The constraints allow up to $2 \times 10^5$ operations, so this solution comfortably fits within limits since sorting dominates and each query is logarithmic.
Test Cases
import sys, io
def run(inp: str) -> str:
sys.stdin = io.StringIO(inp)
import sys
input = sys.stdin.readline
n = int(input())
a = list(map(int, input().split()))
k = int(input())
a.sort()
pref = [0]
for x in a:
pref.append(pref[-1] + x)
from bisect import bisect_right
out = []
for _ in range(k):
L = int(input())
pos = bisect_right(a, L)
cnt = n - pos
total = pref[n] - pref[pos]
out.append(str(total - L * cnt))
return "\n".join(out)
# provided samples
assert run("4\n0 0 0 0\n3\n0\n1\n2\n") == "0\n0\n0"
assert run("5\n4 0 2 1 2\n5\n0\n1\n2\n3\n4\n") == "9\n5\n2\n1\n0"
# custom cases
assert run("1\n10\n3\n0\n5\n10\n") == "10\n5\n0"
assert run("3\n1 2 3\n2\n2\n1\n") == "1\n3"
assert run("6\n5 5 5 5 5 5\n2\n4\n6\n") == "6\n0"
assert run("4\n0 100 0 100\n3\n50\n100\n0\n") == "100\n0\n200"
| Test input | Expected output | What it validates |
|---|---|---|
| single element | decreasing contributions | base case correctness |
| small sorted mix | threshold splitting logic | binary search boundary |
| uniform array | symmetry and linear decay | equal values handling |
| alternating extremes | correctness of suffix aggregation | prefix-suffix correctness |
Edge Cases
A key edge case is when all elements are less than or equal to $L$. In that situation, the suffix is empty and the answer must be zero. The algorithm handles this because pos = n, so both suffix sum and count are zero, and the formula naturally returns zero.
Another case is when $L = 0$. Then every element contributes fully, and the answer becomes the total sum of the array. The binary search returns pos = 0, so the suffix is the entire array and subtraction does nothing.
When all elements are equal, each query either keeps everything or removes everything in a single step. The formula correctly captures this abrupt transition because both suffix sum and count scale together, preserving exact cancellation when $L$ exceeds the value.