Parkour Design
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
Progressive nudges
Open only as much as you need to keep the solve alive.
Think about what adding 1 to a contiguous segment does to the adjacent differences . Which differences actually change?
Only the differences at the two boundaries of the segment change: decreases by 1 (if ) and increases by 1 (if ). All interior and exterior differences are untouched.
Since interior differences don't change, any position with that isn't at a boundary cannot be fixed. If for any , nothing works — answer is 0.
Positions where force (so the left boundary fix brings it to ). Positions where force . If there are 2+ forced left endpoints or 2+ forced right endpoints, they conflict and the answer is 0.
Case-split on how many forced boundaries exist (0, 1, or 2 total). When an endpoint is free, avoid placing it where it would create a new violation: don't put at a position with (would become ), and don't put where (would become ). For the "no bad positions" case, count valid pairs efficiently using a suffix sum over valid right endpoints.
Key Observation
When we add 1 to all elements in segment , the adjacent differences change only at the boundaries:
- Left boundary (, if ): decreases by 1.
- Right boundary (, if ): increases by 1.
- Everything else is unchanged.
This is the crux of the problem. Interior elements all get +1, so their mutual differences don't budge.
Classifying Positions
Since we can only shift boundary differences by exactly 1:
- If for any : impossible, answer is .
- If (must-L position at ): we must have so the left boundary reduces it to .
- If (must-R position at ): we must have so the right boundary brings it to .
If there are must-L positions or must-R positions, they demand conflicting values for the same endpoint → answer is .
Four Cases
Case 1: One must-L at AND one must-R at . The segment is forced to be . Valid iff , giving answer 1 or 0.
Case 2: One must-L at , no must-R. is fixed. ranges from to , but we must avoid where (since would push it over). Count the valid values.
Case 3: No must-L, one must-R at . Symmetric: is fixed. Count valid values from to , avoiding where .
Case 4: No bad positions at all. The "do nothing" option always works (+1 to the answer). For non-empty segments , we need:
- or (left boundary doesn't create a violation)
- or (right boundary doesn't create one)
These conditions depend independently on and (with the constraint ). We compute a suffix sum of valid right endpoints and sweep over valid left endpoints.
Why No Cross-Contamination?
A natural worry: "what if the left boundary creates and then it's also somehow the right boundary?" But , so the left and right boundary positions are always distinct. Boundary effects don't interfere with each other.
Complexity
per test case — one pass to classify, one pass to count.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO(const string& name = "") {
ios::sync_with_stdio(false);
cin.tie(nullptr);
#ifdef ZK_LOCAL_RUN
freopen("f.in", "r", stdin);
freopen("f.out", "w", stdout);
#else
if (!name.empty()) {
freopen((name + ".in").c_str(), "r", stdin);
freopen((name + ".out").c_str(), "w", stdout);
}
#endif
}
int main() {
setIO();
int t;
cin >> t;
while (t--) {
int n;
ll k;
cin >> n >> k;
vector<ll> h(n);
for (auto& x : h) cin >> x;
if (n == 1) {
// No adjacent pairs => everything works: do nothing + [1,1]
cout << 2 << "\n";
continue;
}
int m = n - 1;
vector<ll> d(m);
for (int i = 0; i < m; i++) d[i] = h[i] - h[i + 1];
// If any |d[i]| > k+1, no single +1 segment can fix it
bool impossible = false;
for (int i = 0; i < m; i++) {
if (abs(d[i]) > k + 1) { impossible = true; break; }
}
if (impossible) { cout << 0 << "\n"; continue; }
// must-L: d[i] = k+1 => need l = i+1
// must-R: d[i] = -(k+1) => need r = i
vector<int> mustL, mustR;
for (int i = 0; i < m; i++) {
if (d[i] == k + 1) mustL.push_back(i);
if (d[i] == -(k + 1)) mustR.push_back(i);
}
if ((int)mustL.size() > 1 || (int)mustR.size() > 1) {
cout << 0 << "\n";
continue;
}
if (mustL.size() == 1 && mustR.size() == 1) {
int a = mustL[0], b = mustR[0];
// Segment must be [a+1, b], valid iff a < b
cout << (a < b ? 1 : 0) << "\n";
} else if (mustL.size() == 1) {
// l fixed at mustL[0]+1, count valid r values
int l = mustL[0] + 1; // 0-indexed
ll cnt = 0;
for (int r = l; r < n; r++) {
// Right boundary: if r < n-1, d[r] must not equal k
if (r == n - 1 || d[r] != k) cnt++;
}
cout << cnt << "\n";
} else if (mustR.size() == 1) {
// r fixed at mustR[0], count valid l values
int r = mustR[0]; // 0-indexed
ll cnt = 0;
for (int l = 0; l <= r; l++) {
// Left boundary: if l > 0, d[l-1] must not equal -k
if (l == 0 || d[l - 1] != -k) cnt++;
}
cout << cnt << "\n";
} else {
// No bad positions
// Suffix count of valid right endpoints
vector<ll> suf(n + 1, 0);
for (int r = n - 1; r >= 0; r--) {
suf[r] = suf[r + 1] + (r == n - 1 || d[r] != k ? 1 : 0);
}
ll ans = 1; // "do nothing" option
for (int l = 0; l < n; l++) {
if (l == 0 || d[l - 1] != -k) {
ans += suf[l];
}
}
cout << ans << "\n";
}
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO(const string& name = "") {
ios::sync_with_stdio(false);
cin.tie(nullptr);
#ifdef ZK_LOCAL_RUN
freopen("f.in", "r", stdin);
freopen("f.out", "w", stdout);
#else
if (!name.empty()) {
freopen((name + ".in").c_str(), "r", stdin);
freopen((name + ".out").c_str(), "w", stdout);
}
#endif
}
int main() {
setIO();
int t;
cin >> t;
while (t--) {
int n;
ll k;
cin >> n >> k;
vector<ll> h(n);
for (auto& x : h) cin >> x;
if (n == 1) {
// No adjacent pairs => everything works: do nothing + [1,1]
cout << 2 << "\n";
continue;
}
int m = n - 1;
vector<ll> d(m);
for (int i = 0; i < m; i++) d[i] = h[i] - h[i + 1];
// If any |d[i]| > k+1, no single +1 segment can fix it
bool impossible = false;
for (int i = 0; i < m; i++) {
if (abs(d[i]) > k + 1) { impossible = true; break; }
}
if (impossible) { cout << 0 << "\n"; continue; }
// must-L: d[i] = k+1 => need l = i+1
// must-R: d[i] = -(k+1) => need r = i
vector<int> mustL, mustR;
for (int i = 0; i < m; i++) {
if (d[i] == k + 1) mustL.push_back(i);
if (d[i] == -(k + 1)) mustR.push_back(i);
}
if ((int)mustL.size() > 1 || (int)mustR.size() > 1) {
cout << 0 << "\n";
continue;
}
if (mustL.size() == 1 && mustR.size() == 1) {
int a = mustL[0], b = mustR[0];
// Segment must be [a+1, b], valid iff a < b
cout << (a < b ? 1 : 0) << "\n";
} else if (mustL.size() == 1) {
// l fixed at mustL[0]+1, count valid r values
int l = mustL[0] + 1; // 0-indexed
ll cnt = 0;
for (int r = l; r < n; r++) {
// Right boundary: if r < n-1, d[r] must not equal k
if (r == n - 1 || d[r] != k) cnt++;
}
cout << cnt << "\n";
} else if (mustR.size() == 1) {
// r fixed at mustR[0], count valid l values
int r = mustR[0]; // 0-indexed
ll cnt = 0;
for (int l = 0; l <= r; l++) {
// Left boundary: if l > 0, d[l-1] must not equal -k
if (l == 0 || d[l - 1] != -k) cnt++;
}
cout << cnt << "\n";
} else {
// No bad positions
// Suffix count of valid right endpoints
vector<ll> suf(n + 1, 0);
for (int r = n - 1; r >= 0; r--) {
suf[r] = suf[r + 1] + (r == n - 1 || d[r] != k ? 1 : 0);
}
ll ans = 1; // "do nothing" option
for (int l = 0; l < n; l++) {
if (l == 0 || d[l - 1] != -k) {
ans += suf[l];
}
}
cout << ans << "\n";
}
}
return 0;
}