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.
Try to characterize for one fixed sequence first. The hard version is just asking you to count that characterization over all starts and ends.
A generated final sequence can be seen as a preorder traversal of a forest. Each edge goes from value to value .
While scanning one sequence, only the current ancestor path is usable. Its values are always a consecutive interval . A value is generated iff ; otherwise it starts a new root.
For the total sum, charge each position for the subsegments where starts a new root. If this happens for choices of the left endpoint, its contribution is .
When scanning the whole prefix, if is not active, then every left endpoint pays. If it is active, exactly the left endpoints after the last occurrence of pay. So maintain and last positions.
Research checked: the official Codeforces problem page, the contest editorial page, and kmjp's write-up. The statement is the source of truth here.
First solve the easy question: what is for one sequence ?
Think of the insertion process as building a rooted forest. Every original input element is a root. When we insert immediately after an existing , we create a child of that . The final sequence is a preorder traversal of this forest, and every edge increases the value by exactly .
Now scan a candidate sequence from left to right. At any moment, the only previous elements that can still receive the next node are the active ancestors on the current preorder path. Since every edge adds , their values form a consecutive interval:
For the next value :
After processing , the top of the active path has value , so . If started a new root, then the whole old path is gone and too. Otherwise stays the same.
So for one sequence:
Now move to the hard version. Instead of recomputing this for every subsegment, count each position's contribution.
Fix an index . Let be the number of left endpoints such that, when scanning , the element starts a new root. Once and are fixed, the right endpoint can be any , so position contributes
to the answer.
We just need fast.
Maintain the active interval for the full prefix , using the one-sequence scan above. Also maintain last[x], the latest position where value appeared.
Let
There are two cases.
If , then is not active even when starting from . Starting later cannot magically make an inactive missing parent appear before . Therefore every left endpoint makes a new root:
Then is a new root in the full prefix too, so set .
If , then the active node with value must be the latest occurrence of that value. Let its position be
For every , that occurrence is included and remains active before , so is generated. For every , that occurrence is excluded, and there is no later , so must be a new root. Thus
In both cases, after processing , set and update last[a_i]=i.
The answer is
A couple of edge cases are worth calling out:
The implementation below uses coordinate compression for last, avoiding hash-table nonsense. Complexity is per test case from sorting and binary searches, with memory. Since , this is comfortably accepted.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<ll> a(n + 1), vals;
vals.reserve(n);
for (int i = 1; i <= n; i++) {
cin >> a[i];
vals.push_back(a[i]);
}
sort(vals.begin(), vals.end());
vals.erase(unique(vals.begin(), vals.end()), vals.end());
vector<int> last(vals.size(), 0);
auto id = [&](ll x) {
return int(lower_bound(vals.begin(), vals.end(), x) - vals.begin());
};
const ll INF = (1LL << 62);
ll L = INF, R = -INF;
ll ans = 0;
for (int i = 1; i <= n; i++) {
ll need = a[i] - 1;
ll starts;
if (!(L <= need && need <= R)) {
starts = i;
L = a[i];
} else {
starts = i - last[id(need)];
}
R = a[i];
last[id(a[i])] = i;
ans += starts * (n - i + 1LL);
}
cout << ans << '\n';
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<ll> a(n + 1), vals;
vals.reserve(n);
for (int i = 1; i <= n; i++) {
cin >> a[i];
vals.push_back(a[i]);
}
sort(vals.begin(), vals.end());
vals.erase(unique(vals.begin(), vals.end()), vals.end());
vector<int> last(vals.size(), 0);
auto id = [&](ll x) {
return int(lower_bound(vals.begin(), vals.end(), x) - vals.begin());
};
const ll INF = (1LL << 62);
ll L = INF, R = -INF;
ll ans = 0;
for (int i = 1; i <= n; i++) {
ll need = a[i] - 1;
ll starts;
if (!(L <= need && need <= R)) {
starts = i;
L = a[i];
} else {
starts = i - last[id(need)];
}
R = a[i];
last[id(a[i])] = i;
ans += starts * (n - i + 1LL);
}
cout << ans << '\n';
}
}