Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
The answer is not just total sum modulo . When the buffer clears, the extra amount is thrown away too, so you need the exact greedy cut points. Annoying, but fair.
Think of each clear as ending a segment. Starting at some index, the next segment is the shortest prefix whose sum is at least .
Because is non-increasing, the lengths of those clearing segments never decrease. If one segment has length , then the next elements are no bigger than the previous segment's first elements, whose sum was still .
For a fixed length and threshold , all starts whose length- sum is at least form a prefix of positions. So precompute the rightmost valid start for each small and each query threshold.
During a query, scan segment lengths . If the current start is at most that rightmost valid start, the next segment length is exactly , and you can batch many such segments. After , every remaining clear consumes more than elements, so prefix-sum binary searches are cheap enough.
Think of the process as cutting the query range into greedy clearing segments.
Starting from position , the next clear happens at the smallest position such that
Then everything in that segment is gone, including the overshoot. So yeah, this is not modulo arithmetic. The final answer is the number of full greedy segments plus the sum of the leftover suffix that never reaches .
The Key Lemma
The lengths of clearing segments never decrease.
Suppose one clearing segment starts at and has length . By minimality,
Now look at the first elements after this segment:
Since the array is non-increasing, each of these is at most the corresponding earlier element:
So their sum is also . Therefore the next segment cannot clear in fewer than elements. Segment lengths are monotone nondecreasing. That is the whole problem cracking open.
Precomputing Small Segment Lengths
Pick a constant around .
For every length and every threshold that appears in the input, precompute:
rightmost[d][x] = the largest starting position such that
For fixed , length- window sums decrease as the start moves right, again because the array is non-increasing. So valid starts form a prefix: if start works, every earlier start works too.
We only need query thresholds, not all possible up to . Sort and compress all query values of .
For each , scan starts from right to left. As window sums increase, assign compressed thresholds in increasing order. The first time a threshold becomes possible is exactly its rightmost valid start.
Answering A Query
For query , let cur be the next unprocessed index.
We scan possible segment lengths .
Before processing length , all future clearing segments have length at least . This is true because segment lengths never decrease, and we already handled all smaller lengths.
Now check rightmost[d][x].
If cur <= rightmost[d][x], then elements from cur already reach . Since no smaller length can work anymore, the next clearing segment has length exactly .
Even better, several consecutive segments may also have length . Their starts are
Each works while the start is at most rightmost[d][x], and the segment must also fit inside . So the number we can batch is
Add that many clears and advance cur by that many times .
Handling Long Segments
After all are processed, every remaining clear, if it exists, consumes more than elements.
So there can be only of them in one query. For each one, use prefix sums and binary search to find the first endpoint where the sum reaches .
When the remaining total sum is less than , no more clear happens. That remaining sum is the second output value.
Complexity
Let be the number of distinct query thresholds in one test case.
Precomputation costs
Each query costs
Memory is
With and the given limits, this fits comfortably. Big array, big hammer, no nonsense.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Query {
int l, r, id;
ll x;
};
int main() {
setIO();
int t;
cin >> t;
const int SMALL = 1024;
while (t--) {
int n, q;
cin >> n >> q;
vector<ll> a(n + 1), pref(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
pref[i] = pref[i - 1] + a[i];
}
vector<Query> queries(q);
vector<ll> values;
values.reserve(q);
for (int i = 0; i < q; i++) {
cin >> queries[i].l >> queries[i].r >> queries[i].x;
values.push_back(queries[i].x);
}
sort(values.begin(), values.end());
values.erase(unique(values.begin(), values.end()), values.end());
for (auto &query : queries) {
query.id = lower_bound(values.begin(), values.end(), query.x) - values.begin();
}
int k = (int)values.size();
int limit = min(SMALL, n);
int stride = limit + 1;
vector<int> rightmost((long long)k * stride, 0);
for (int len = 1; len <= limit; len++) {
int ptr = 0;
for (int start = n - len + 1; start >= 1 && ptr < k; start--) {
ll sum = pref[start + len - 1] - pref[start - 1];
while (ptr < k && values[ptr] <= sum) {
rightmost[(long long)ptr * stride + len] = start;
ptr++;
}
}
}
for (const auto &query : queries) {
int cur = query.l;
int r = query.r;
int clears = 0;
int base = query.id * stride;
for (int len = 1; len <= limit && cur <= r; len++) {
int last = rightmost[base + len];
if (last >= cur) {
int usable = min(r - cur + 1, last - cur + len);
int take = usable / len;
clears += take;
cur += take * len;
}
}
int before = cur - 1;
while (before < r && pref[r] - pref[before] >= query.x) {
clears++;
ll need = pref[before] + query.x;
int lo = before + 1, hi = r;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (pref[mid] >= need) hi = mid;
else lo = mid + 1;
}
before = lo;
}
cout << clears << ' ' << pref[r] - pref[before] << '\n';
}
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Query {
int l, r, id;
ll x;
};
int main() {
setIO();
int t;
cin >> t;
const int SMALL = 1024;
while (t--) {
int n, q;
cin >> n >> q;
vector<ll> a(n + 1), pref(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
pref[i] = pref[i - 1] + a[i];
}
vector<Query> queries(q);
vector<ll> values;
values.reserve(q);
for (int i = 0; i < q; i++) {
cin >> queries[i].l >> queries[i].r >> queries[i].x;
values.push_back(queries[i].x);
}
sort(values.begin(), values.end());
values.erase(unique(values.begin(), values.end()), values.end());
for (auto &query : queries) {
query.id = lower_bound(values.begin(), values.end(), query.x) - values.begin();
}
int k = (int)values.size();
int limit = min(SMALL, n);
int stride = limit + 1;
vector<int> rightmost((long long)k * stride, 0);
for (int len = 1; len <= limit; len++) {
int ptr = 0;
for (int start = n - len + 1; start >= 1 && ptr < k; start--) {
ll sum = pref[start + len - 1] - pref[start - 1];
while (ptr < k && values[ptr] <= sum) {
rightmost[(long long)ptr * stride + len] = start;
ptr++;
}
}
}
for (const auto &query : queries) {
int cur = query.l;
int r = query.r;
int clears = 0;
int base = query.id * stride;
for (int len = 1; len <= limit && cur <= r; len++) {
int last = rightmost[base + len];
if (last >= cur) {
int usable = min(r - cur + 1, last - cur + len);
int take = usable / len;
clears += take;
cur += take * len;
}
}
int before = cur - 1;
while (before < r && pref[r] - pref[before] >= query.x) {
clears++;
ll need = pref[before] + query.x;
int lo = before + 1, hi = r;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (pref[mid] >= need) hi = mid;
else lo = mid + 1;
}
before = lo;
}
cout << clears << ' ' << pref[r] - pref[before] << '\n';
}
}
return 0;
}