D - 高橋君の価格調整 / Takahashi's Price Adjustment Editorial by admin Editorial for printing
gemini-3.5-flash-thinkingOverview
There are \(N\) products, each with a given initial price. You are given \(Q\) queries that select the product with the maximum or minimum price and update its price. However, some queries may be cancelled (effectively leaving the price unchanged). The problem asks you to find the total price of all products after processing all queries.
To efficiently perform dynamic value updates and retrieve the overall maximum/minimum values (along with their indices), this problem can be solved using a Segment Tree.
Analysis
Naive Approach and Its Limitations
Consider a method (simulation) that scans the entire array to find the maximum or minimum value for each query. In this case, each query takes \(O(N)\) time. Since there are \(Q\) queries, the overall time complexity is \(O(NQ)\). In this problem, \(N, Q \le 2 \times 10^5\), so in the worst case, approximately \(4 \times 10^{10}\) operations would be needed, resulting in a Time Limit Exceeded (TLE) verdict.
Therefore, we need a data structure that can efficiently perform the following operations: 1. Identify the index of the product with the highest (or lowest) price among all products. 2. Update the price of the identified product to a new value.
Handling the Cancellation Flag \(R_j\)
Some queries include an instruction to cancel the price change (\(R_j = 1\)). Reading the procedure carefully, we can observe the following: - When \(R_j = 1\), the product selection (step 1) is performed, but the price is ultimately restored to its original value. - In other words, queries with \(R_j = 1\) have absolutely no effect on other products’ prices or future queries.
Therefore, when processing queries, we can simplify as follows: - When \(R_j = 0\): Identify the target product and update its price to \(X_j\). - When \(R_j = 1\): Only identify the target product; do not update the price (do nothing).
Algorithm
To perform dynamic retrieval and updating of maximum/minimum values in logarithmic time \(O(\log N)\), we use a Segment Tree.
Segment Tree Design
Each node of the segment tree stores the following information about its interval:
max_val: Maximum price in the intervalmax_idx: The smallest product number (index) among products with the maximum pricemin_val: Minimum price in the intervalmin_idx: The smallest product number (index) among products with the minimum price
Merging Two Intervals (op Function)
We define the operation op(a, b) that merges information from two child nodes a and b to produce the parent node’s information.
- Maximum value update:
- If
a.max_val > b.max_val, adopta’s information. - If
a.max_val < b.max_val, adoptb’s information. - If the values are equal, adopt the one with the smaller index (
min(a.max_idx, b.max_idx)).
- If
- Minimum value update:
- If
a.min_val < b.min_val, adopta’s information. - If
a.min_val > b.min_val, adoptb’s information. - If the values are equal, adopt the one with the smaller index (
min(a.min_idx, b.min_idx)).
- If
Identity Element (e Function)
We define the identity element e() used for segment tree initialization and out-of-range references.
- To avoid interfering with maximum comparisons, max_val is set to a sufficiently small value (e.g., \(-1\)).
- To avoid interfering with minimum comparisons, min_val is set to a sufficiently large value (e.g., \(2 \times 10^{18}\) or infinity).
- Indices are set to a sufficiently large value (e.g., \(10^9\)) so they won’t be selected by min operations.
Query Processing Procedure
- Retrieve the information from the root (entire range) of the segment tree using
seg.all_prod(). - If \(T_j = 1\), set the target product index
idxtomax_idx; if \(T_j = 2\), set it tomin_idx. - Only when \(R_j = 0\), update the product’s price to \(X_j\).
- Update the
idx-th element of the segment tree with new price information{X, idx, X, idx}. - Also update the price
A[idx]in the original array to \(X_j\) for computing the final total.
- Update the
Finally, compute and output the sum of array A.
Complexity
- Time Complexity: \(O(N + Q \log N)\)
- Building the segment tree takes \(O(N)\).
- For each query, retrieving the overall maximum/minimum takes \(O(1)\), and updating a value (when \(R_j = 0\)) takes \(O(\log N)\). Since there are \(Q\) queries, the total query processing takes \(O(Q \log N)\).
- Computing the final total price takes \(O(N)\).
- Space Complexity: \(O(N)\)
- Since we maintain an array of size \(N\) and a segment tree, the space complexity is \(O(N)\), which is well within the memory limit.
Implementation Notes
Tiebreaking: The rule states “if prices are equal, select the product with the smallest product number.” Therefore, in the merge function
op, the handling of takingmin(a.idx, b.idx)when values are equal is extremely important.Identity element values: The value set for
min_valin the identity elemente()must be sufficiently larger than the maximum possible product price (\(10^9\)). In C++, use thelong longtype to prevent overflow and set a sufficiently large value.Source Code
#include <iostream>
#include <vector>
#include <algorithm>
#include <atcoder/segtree>
using namespace std;
using namespace atcoder;
struct S {
long long max_val;
int max_idx;
long long min_val;
int min_idx;
};
S op(S a, S b) {
S res;
if (a.max_val > b.max_val) {
res.max_val = a.max_val;
res.max_idx = a.max_idx;
} else if (a.max_val < b.max_val) {
res.max_val = b.max_val;
res.max_idx = b.max_idx;
} else {
res.max_val = a.max_val;
res.max_idx = min(a.max_idx, b.max_idx);
}
if (a.min_val < b.min_val) {
res.min_val = a.min_val;
res.min_idx = a.min_idx;
} else if (a.min_val > b.min_val) {
res.min_val = b.min_val;
res.min_idx = b.min_idx;
} else {
res.min_val = a.min_val;
res.min_idx = min(a.min_idx, b.min_idx);
}
return res;
}
S e() {
return S{-1, 1000000000, 2000000000000000000LL, 1000000000};
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int N, Q;
if (!(cin >> N >> Q)) return 0;
vector<long long> A(N);
vector<S> init(N);
for (int i = 0; i < N; ++i) {
cin >> A[i];
init[i] = S{A[i], i, A[i], i};
}
segtree<S, op, e> seg(init);
for (int j = 0; j < Q; ++j) {
int T;
long long X;
int R;
cin >> T >> X >> R;
S root = seg.all_prod();
int idx = (T == 1) ? root.max_idx : root.min_idx;
if (R == 0) {
A[idx] = X;
seg.set(idx, S{X, idx, X, idx});
}
}
long long ans = 0;
for (int i = 0; i < N; ++i) {
ans += A[i];
}
cout << ans << "\n";
return 0;
}
This editorial was generated by gemini-3.5-flash-thinking.
posted:
last update: