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.
Do not try to rebuild every subarray. Look at one value at a time and ask: how many subarrays ending at position contain it?
If a value's last occurrence in the prefix is position , then among subarrays ending at , it appears exactly when the start is .
So is just the sum of the current last occurrence positions of all distinct values seen so far. That is the entire problem wearing a fake mustache.
Now compare with . When you append , either you create a new value and add , or you move some old value's last occurrence from to , adding .
Let . If , make a new number. Otherwise, set and copy the value whose current last occurrence is exactly .
Let be the last position where value appears inside the prefix . If has not appeared yet, ignore it.
The important question is: for fixed ending position , how much does one value contribute to
Suppose the last occurrence of up to position is . Then appears in subarray exactly when . There are exactly such starts: .
So value contributes to . Summing over all distinct values seen so far gives:
That is the key observation. The scary definition with a pile of subarrays collapses into a very chill sum of last positions.
What changes when we append position ?
Let
Only the value can change the sum of last positions.
There are two cases:
is a brand-new value.
Then it did not contribute before, and now its last position is . So:
already appeared before.
Say its previous last occurrence was . In the sum, this value used to contribute , and now it contributes . So:
Therefore:
This means tells us exactly what kind of move to make.
Construction
Process positions from left to right. Maintain owner[p], the value whose current last occurrence is position . Only current last positions matter.
For each position :
owner[p], put it at , and move its last occurrence from to .Because the statement guarantees that some answer exists, whenever we need owner[p], it will exist. No need for detective work, no need for backtracking, no cursed DP.
We use new labels as needed. At most labels are created, so every stays between and .
Why this is correct
We maintain the invariant that after processing position , owner[p] correctly records which value has current last occurrence , and the constructed prefix produces the given .
Initially, nothing exists and the sum is .
At step :
So after the step, the sum becomes . The invariant continues to hold.
Thus the final array produces exactly the given .
Complexity
Each position is processed once, with work. Total complexity is per test case, and memory usage is .
#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<int> a(n + 1), owner(n + 1, 0);
ll prev = 0;
int nextValue = 0;
for (int i = 1; i <= n; i++) {
ll b;
cin >> b;
ll d = b - prev;
int value;
if (d == i) {
value = ++nextValue;
} else {
int p = (int)(i - d);
value = owner[p];
owner[p] = 0;
}
a[i] = value;
owner[i] = value;
prev = b;
}
for (int i = 1; i <= n; i++) {
if (i > 1) cout << ' ';
cout << a[i];
}
cout << '\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<int> a(n + 1), owner(n + 1, 0);
ll prev = 0;
int nextValue = 0;
for (int i = 1; i <= n; i++) {
ll b;
cin >> b;
ll d = b - prev;
int value;
if (d == i) {
value = ++nextValue;
} else {
int p = (int)(i - d);
value = owner[p];
owner[p] = 0;
}
a[i] = value;
owner[i] = value;
prev = b;
}
for (int i = 1; i <= n; i++) {
if (i > 1) cout << ' ';
cout << a[i];
}
cout << '\n';
}
}