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.
For one fraction starting as , after operations the final value only depends on the final denominator. If the denominator becomes , then the numerator became , so the value is .
That expression immediately gives a piecewise optimum: if , keep the denominator at and get ; if , push the denominator to and get .
In a whole subarray, all operations should go to one occurrence of the minimum value. Smaller denominators have at least as good marginal gains, and they reach the big -per-operation phase earlier. Splitting operations is bait.
So every subarray contributes its ordinary base sum, except one chosen minimum element is replaced by the piecewise value above. Assign each subarray to exactly one minimum occurrence using a monotonic stack tie rule.
For an element that is the assigned minimum in subarrays, its contribution as the special element is linear in : for , and for . Since the queries are sorted, add these two linear functions to ranges of query indices with difference arrays.
Research basis: I checked the official Codeforces editorial page for Educational Round 188, the CF Step hints page, and a Codeforces blog post pointing to a video editorial: https://codeforces.com/blog/entry/152150, https://cfstep.com/codeforces/contests/contest-2204/problem-f/hints/, and https://codeforces.com/blog/entry/152250. The official tutorial text was not expanded on the page, but the official accepted code confirms the monotonic-stack contribution split and the same piecewise linear formula. The statement below is the real source of truth.
First solve the tiny problem: one fraction , exactly operations.
Suppose the final denominator is . We decreased the denominator times, so the numerator was increased
times. The final numerator is therefore
and the value is
Now the choice is obvious:
At the boundary , both give , so using the implementation split
Now take a subarray. The important claim is that all operations should be spent on one fraction whose initial denominator is minimum in the subarray.
Why? For denominator , the marginal gains of the optimized value are
If , then the marginal sequence for dominates the marginal sequence for at every position: , and denominator reaches the -gain phase no later than denominator . Also the sequence is nondecreasing, so moving operations from a larger denominator to the smallest denominator never hurts. Splitting operations across fractions sounds democratic; mathematically, it is just leaving money on the table.
Therefore, for a subarray with minimum value , pick one occurrence of that minimum and replace its base contribution by . Every other element just contributes its original .
We need sum this over all subarrays without doing work.
Assign every subarray to exactly one position containing its minimum. Use this tie rule: assign to the leftmost minimum. For each index :
Then is the assigned minimum exactly for subarrays with
so the count is
These arrays come from two monotonic stacks. The asymmetric tie handling is not optional bookkeeping nonsense; it prevents duplicate equal minima from double-counting the same subarray.
Now split the answer into two parts.
For position , the total number of subarrays containing it is
In of them, is the special minimum and should be handled by . In all remaining subarrays containing , it contributes normally as . So define
This part is independent of .
For the special-minimum part, each index contributes over its assigned subarrays:
Both branches are linear in . Since the queries are sorted, find
by binary search. Add one linear function to query range and the other to using difference arrays for slope and constant term.
For :
For :
After applying all range additions, prefix the slope and constant arrays. For each query :
Edge cases:
Complexity is time from modular inverses and binary searches, and memory. With elements, this is fine.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const ll MOD = 998244353;
ll norm(ll x) {
x %= MOD;
if (x < 0) x += MOD;
return x;
}
ll modPow(ll a, ll e) {
ll r = 1;
a %= MOD;
while (e > 0) {
if (e & 1) r = r * a % MOD;
a = a * a % MOD;
e >>= 1;
}
return r;
}
void addRange(vector<ll>& diff, int l, int r, ll val) {
if (l >= r) return;
diff[l] = norm(diff[l] + val);
diff[r] = norm(diff[r] - val);
}
int main() {
setIO();
int n, m;
cin >> n >> m;
vector<int> a(n), k(m);
for (int &x : a) cin >> x;
for (int &x : k) cin >> x;
vector<int> L(n), R(n), st;
st.reserve(n);
for (int i = 0; i < n; i++) {
while (!st.empty() && a[st.back()] > a[i]) st.pop_back();
L[i] = st.empty() ? -1 : st.back();
st.push_back(i);
}
st.clear();
for (int i = n - 1; i >= 0; i--) {
while (!st.empty() && a[st.back()] >= a[i]) st.pop_back();
R[i] = st.empty() ? n : st.back();
st.push_back(i);
}
vector<ll> slope(m + 1), constant(m + 1);
ll common = 0;
for (int i = 0; i < n; i++) {
ll cnt = 1LL * (i - L[i]) * (R[i] - i) % MOD;
ll all = 1LL * (i + 1) * (n - i) % MOD;
ll ordinary = norm(all - cnt);
ll inv = modPow(a[i], MOD - 2);
common = (common + ordinary * inv) % MOD;
int p = lower_bound(k.begin(), k.end(), a[i]) - k.begin();
ll smallSlope = cnt * inv % MOD;
ll smallConst = smallSlope;
addRange(slope, 0, p, smallSlope);
addRange(constant, 0, p, smallConst);
ll bigSlope = cnt;
ll bigConst = cnt * norm(2LL - a[i]) % MOD;
addRange(slope, p, m, bigSlope);
addRange(constant, p, m, bigConst);
}
ll curSlope = 0, curConst = 0;
for (int i = 0; i < m; i++) {
curSlope = norm(curSlope + slope[i]);
curConst = norm(curConst + constant[i]);
ll ans = norm(common + curSlope * (k[i] % MOD) + curConst);
cout << ans << '\n';
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const ll MOD = 998244353;
ll norm(ll x) {
x %= MOD;
if (x < 0) x += MOD;
return x;
}
ll modPow(ll a, ll e) {
ll r = 1;
a %= MOD;
while (e > 0) {
if (e & 1) r = r * a % MOD;
a = a * a % MOD;
e >>= 1;
}
return r;
}
void addRange(vector<ll>& diff, int l, int r, ll val) {
if (l >= r) return;
diff[l] = norm(diff[l] + val);
diff[r] = norm(diff[r] - val);
}
int main() {
setIO();
int n, m;
cin >> n >> m;
vector<int> a(n), k(m);
for (int &x : a) cin >> x;
for (int &x : k) cin >> x;
vector<int> L(n), R(n), st;
st.reserve(n);
for (int i = 0; i < n; i++) {
while (!st.empty() && a[st.back()] > a[i]) st.pop_back();
L[i] = st.empty() ? -1 : st.back();
st.push_back(i);
}
st.clear();
for (int i = n - 1; i >= 0; i--) {
while (!st.empty() && a[st.back()] >= a[i]) st.pop_back();
R[i] = st.empty() ? n : st.back();
st.push_back(i);
}
vector<ll> slope(m + 1), constant(m + 1);
ll common = 0;
for (int i = 0; i < n; i++) {
ll cnt = 1LL * (i - L[i]) * (R[i] - i) % MOD;
ll all = 1LL * (i + 1) * (n - i) % MOD;
ll ordinary = norm(all - cnt);
ll inv = modPow(a[i], MOD - 2);
common = (common + ordinary * inv) % MOD;
int p = lower_bound(k.begin(), k.end(), a[i]) - k.begin();
ll smallSlope = cnt * inv % MOD;
ll smallConst = smallSlope;
addRange(slope, 0, p, smallSlope);
addRange(constant, 0, p, smallConst);
ll bigSlope = cnt;
ll bigConst = cnt * norm(2LL - a[i]) % MOD;
addRange(slope, p, m, bigSlope);
addRange(constant, p, m, bigConst);
}
ll curSlope = 0, curConst = 0;
for (int i = 0; i < m; i++) {
curSlope = norm(curSlope + slope[i]);
curConst = norm(curConst + constant[i]);
ll ans = norm(common + curSlope * (k[i] % MOD) + curConst);
cout << ans << '\n';
}
}