E - 家計簿と目標残高 / Household Budget and Target Balance Editorial by admin Editorial for printing
gpt-5.3-codexOverview
If we convert each transaction into a sequence where “deposit is \(+B_i\), withdrawal is \(-B_i\)”, then query 2 K becomes the problem of “finding the smallest position where the prefix sum first reaches \(K\) or more.”
To efficiently handle updates (point modifications) and this search, we use a segment tree that maintains interval sums and maximum prefix sums.
Analysis
First, we replace the transactions with an array \(A\) as follows:
R b\(\Rightarrow A_i = +b\)L b\(\Rightarrow A_i = -b\)
Then, the balance after processing the \(i\)-th transaction is the prefix sum
[
S_i = A_1 + A_2 + \cdots + A_i
]
Query 2 K asks “find the smallest \(i\) such that \(S_i \ge K\) (or output 0 if none exists).”
Naively scanning the prefix sum from the beginning each time costs \(O(N)\) per query.
Since \(Q\) can be up to \(2\times 10^5\), the worst case becomes \(O(NQ)\), which is too slow.
Because there are also updates 1 p D b, we cannot rely solely on precomputation.
What we need is a data structure that supports both:
- Point updates (modifying \(A_p\))
- Fast search for “the first position where the prefix sum exceeds \(K\) when scanning from the beginning”
The key insight is to use a segment tree where each node stores:
- sum: the interval sum
- pref: the maximum prefix sum within that interval
When merging two child intervals \(L, R\): [ \text{sum} = L.\text{sum} + R.\text{sum} ] [ \text{pref} = \max(L.\text{pref},\ L.\text{sum} + R.\text{pref}) ] This is because “the maximum prefix either ends entirely within the left child, or uses the entire left child and extends into the right child” — there are only two cases.
Algorithm
- Build array \(A\) from the initial transactions (
Ris positive,Lis negative). - Construct the segment tree. Each leaf has:
sum = A_ipref = max(0, A_i)
- Process operations in order:
1 p D b
Update \(A_p\) to the new value (\(+b\) ifR, \(-b\) ifL), and perform a point update on the segment tree.2 K
First check the root’spref. If it is less than \(K\), the target is unreachable, so output0.
Otherwise, descend from the root:- Let
accbe the accumulated prefix sum so far. - Look at the left child: if
acc + left.pref >= K, the answer is in the left subtree, so go left. - Otherwise, set
acc += left.sumand go right. - The leaf position reached is “the first transaction number where the prefix sum reaches \(K\) or more.”
- Let
The key point is that this descent always maintains “the earliest position.”
Complexity
- Time complexity: \(O((N+Q)\log N)\)
- Construction \(O(N)\), each update/query is \(O(\log N)\)
- Space complexity: \(O(N)\)
Implementation Notes
Since amounts and prefix sums can become very large, use
long long.The problem uses 1-indexed answers, so add
+1when outputting from internal 0-indexed representation.Since the leaf’s
prefis set tomax(0, A_i), unused positions (with value 0) are handled naturally.By checking
root.pref < Kfirst infirstAtLeast, the unreachable case can be handled concisely.Source Code
#include <bits/stdc++.h>
using namespace std;
struct SegTree {
struct Node {
long long sum;
long long pref; // maximum prefix sum
};
int n;
vector<Node> st;
static Node mergeNode(const Node& a, const Node& b) {
return Node{
a.sum + b.sum,
max(a.pref, a.sum + b.pref)
};
}
SegTree(const vector<long long>& arr) {
int sz = (int)arr.size();
n = 1;
while (n < sz) n <<= 1;
st.assign(2 * n, Node{0LL, 0LL});
for (int i = 0; i < sz; i++) {
st[n + i] = Node{arr[i], max(0LL, arr[i])};
}
for (int i = n - 1; i >= 1; i--) {
st[i] = mergeNode(st[i << 1], st[i << 1 | 1]);
}
}
void pointUpdate(int idx, long long val) {
int p = idx + n;
st[p] = Node{val, max(0LL, val)};
p >>= 1;
while (p) {
st[p] = mergeNode(st[p << 1], st[p << 1 | 1]);
p >>= 1;
}
}
// return first index i (0-based) such that prefix sum up to i >= K, else -1
int firstAtLeast(long long K) const {
if (st[1].pref < K) return -1;
int v = 1;
long long acc = 0;
while (v < n) {
const Node& L = st[v << 1];
if (acc + L.pref >= K) {
v = v << 1;
} else {
acc += L.sum;
v = v << 1 | 1;
}
}
return v - n;
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N, Q;
cin >> N >> Q;
vector<char> D(N);
vector<long long> B(N);
vector<long long> A(N);
for (int i = 0; i < N; i++) {
cin >> D[i] >> B[i];
A[i] = (D[i] == 'R' ? B[i] : -B[i]);
}
SegTree seg(A);
for (int qi = 0; qi < Q; qi++) {
int t;
cin >> t;
if (t == 1) {
int p;
char nd;
long long b;
cin >> p >> nd >> b;
--p;
D[p] = nd;
B[p] = b;
long long v = (nd == 'R' ? b : -b);
seg.pointUpdate(p, v);
} else {
long long K;
cin >> K;
int idx = seg.firstAtLeast(K);
if (idx == -1 || idx >= N) cout << 0 << '\n';
else cout << (idx + 1) << '\n';
}
}
return 0;
}
This editorial was generated by gpt-5.3-codex.
posted:
last update: