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.
A subarray only cares about the differences between neighboring elements inside it. So stop staring at the values directly and build for .
A subarray with is -exquisite exactly when every edge is at least .
For a fixed , mark positions where . Then every valid subarray corresponds to a contiguous block of marked positions, i.e. an interval of active edges.
If an active component has length edges, it contributes valid subarrays. The answer is the sum of this over all active components.
Process from down to . When decreases, only edges with become active. Use DSU over edge positions to merge neighboring active edges and maintain the total contribution dynamically.
Let
A subarray with is -exquisite iff
So the actual array values are mostly a distraction after we compute adjacent differences. Classic Codeforces move: reduce the problem to edges, and suddenly the scary statement is just wearing a fake mustache.
For a fixed , call an edge position active if . Then valid subarrays are exactly contiguous intervals of active edge positions.
If we have a maximal active block of consecutive edges, how many subarrays does it create? Any nonempty interval of edges inside that block corresponds to exactly one subarray of values. The number of nonempty intervals in a length- block is
Therefore, for each , the answer is
Now we need all , not just one. Recomputing blocks every time would be , which is dead on arrival.
Instead, process thresholds in descending order:
When moving from to , the only new active edges are positions where . No active edge ever disappears. This screams DSU.
Maintain connected components of active edge positions . Each component is a consecutive block, because edges only connect to immediate neighbors. Also maintain a running value :
When activating a new edge position :
When merging two components of sizes and , remove their old contributions and add the merged one:
After activating all edges with , we have exactly the active edges satisfying , so .
Why this is correct:
Edge cases are clean:
long long.Complexity per test case is
with DSU, or basically linear unless your inverse Ackermann function starts doing cardio. The total input size is bounded by , so this easily fits.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
ll ways(ll x) {
return x * (x + 1) / 2;
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> p(n + 1);
for (int i = 1; i <= n; i++) cin >> p[i];
vector<vector<int>> at(n);
for (int i = 1; i < n; i++) {
int d = abs(p[i] - p[i + 1]);
at[d].push_back(i);
}
vector<int> parent(n), sz(n, 1), active(n, 0);
iota(parent.begin(), parent.end(), 0);
auto find = [&](auto&& self, int x) -> int {
if (parent[x] == x) return x;
return parent[x] = self(self, parent[x]);
};
ll cur = 0;
auto unite = [&](int a, int b) {
a = find(find, a);
b = find(find, b);
if (a == b) return;
if (sz[a] < sz[b]) swap(a, b);
cur -= ways(sz[a]);
cur -= ways(sz[b]);
parent[b] = a;
sz[a] += sz[b];
cur += ways(sz[a]);
};
vector<ll> ans(n);
for (int k = n - 1; k >= 1; k--) {
for (int pos : at[k]) {
active[pos] = 1;
cur += 1;
if (pos > 1 && active[pos - 1]) unite(pos, pos - 1);
if (pos + 1 < n && active[pos + 1]) unite(pos, pos + 1);
}
ans[k] = cur;
}
for (int k = 1; k < n; k++) {
cout << ans[k] << ' ';
}
cout << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
ll ways(ll x) {
return x * (x + 1) / 2;
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> p(n + 1);
for (int i = 1; i <= n; i++) cin >> p[i];
vector<vector<int>> at(n);
for (int i = 1; i < n; i++) {
int d = abs(p[i] - p[i + 1]);
at[d].push_back(i);
}
vector<int> parent(n), sz(n, 1), active(n, 0);
iota(parent.begin(), parent.end(), 0);
auto find = [&](auto&& self, int x) -> int {
if (parent[x] == x) return x;
return parent[x] = self(self, parent[x]);
};
ll cur = 0;
auto unite = [&](int a, int b) {
a = find(find, a);
b = find(find, b);
if (a == b) return;
if (sz[a] < sz[b]) swap(a, b);
cur -= ways(sz[a]);
cur -= ways(sz[b]);
parent[b] = a;
sz[a] += sz[b];
cur += ways(sz[a]);
};
vector<ll> ans(n);
for (int k = n - 1; k >= 1; k--) {
for (int pos : at[k]) {
active[pos] = 1;
cur += 1;
if (pos > 1 && active[pos - 1]) unite(pos, pos - 1);
if (pos + 1 < n && active[pos + 1]) unite(pos, pos + 1);
}
ans[k] = cur;
}
for (int k = 1; k < n; k++) {
cout << ans[k] << ' ';
}
cout << '\n';
}
return 0;
}