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.
Fix the right endpoint and call the answer for by . First prove the monotonicity: moving to the right cannot make the optimum worse, so .
A segment whose cost is is never sacred. You can split it into two segments with costs at most and , and the total does not increase. So in some optimal partition, every segment has cost only , , or .
Precompute : the smallest start such that the segment has cost at most . The condition is just , so is one plus the last position with .
For fixed , has only possible values. Flip the DP: instead of storing every , store , the earliest such that .
To compute , choose the first segment ending at some with cost at most , then the suffix must fit budget . Thus query the minimum over with RMQ.
Let be the minimum partition cost of the subarray . We need
The direct DP is obvious but useless:
where . That is too many states if we store all for every .
Two key facts
First, for fixed , is monotone:
Why? Removing the first element from a segment can only increase its minimum, so the segment cost cannot increase. In the DP above, every candidate for starting at is at least as expensive as the corresponding candidate after deleting . So later starts are never worse.
Second, we only need segment costs in an optimal partition. Suppose a segment has cost . Pick and . Then
Let the segment end with value . Cut it right before the suffix where all values are at least . The right part has cost at most . The left part has last value below , while the old whole segment had minimum large enough relative to , so the left part has cost at most . The two new costs sum to at most , and both are smaller than . Repeat this cleanup until no segment has cost above .
So the scary cost function gets kneecapped: every transition only needs .
The value range is tiny
For any fixed subarray ending at , there is a crude greedy partition with cost at most .
Build blocks from right to left. Suppose the current block must end at value . Take the longest suffix whose minimum is at least ; that block has cost at most . If this does not consume the whole remaining prefix, the next right endpoint is a value strictly below . So the endpoint value halves each time. Since , this happens at most about times, each costing at most . Hence is safely above every possible optimum.
This is the whole trick: ranges over positions, but the answer value ranges over only values. Flip the DP, knapsack-style.
Precomputing segment starts
Define
For the segment ,
is equivalent to
So every value in the segment must be at least . Therefore is exactly one plus the last position with
or equivalently .
We compute all for using a segment tree that finds the rightmost position with value below a threshold. Then build sparse tables over each so range minimum queries are .
The flipped DP
For fixed , define
If no such exists, set . Also , since nonempty subarrays cost at least .
Because is monotone in , starts with value at most form a suffix:
So the number of starts with exact value is
That gives the contribution for this once all are known.
Now compute . Choose the first segment of the partition: it is , and let its cost budget be . We must have ; do not let a cost- segment sneak into budget , unless you enjoy getting slapped by the test .
The remaining suffix must have optimum at most . This means
so
For any valid , the earliest possible for the first segment is . Therefore
The outer just says if a start works with smaller budget, it also works with budget .
Each inner minimum is one RMQ. There are only budgets and costs, so each right endpoint costs constant-ish time.
Complexity
Precomputing and sparse tables costs . The DP costs , which is just linear with a chunky constant. Memory is .
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const int C = 128;
struct SegTree {
int n;
vector<ll> mn;
SegTree(const vector<ll>& a) {
init(a);
}
void init(const vector<ll>& a) {
n = (int)a.size() - 1;
mn.assign(4 * n + 4, 0);
build(1, 1, n, a);
}
void build(int v, int l, int r, const vector<ll>& a) {
if (l == r) {
mn[v] = a[l];
return;
}
int m = (l + r) / 2;
build(v * 2, l, m, a);
build(v * 2 + 1, m + 1, r, a);
mn[v] = min(mn[v * 2], mn[v * 2 + 1]);
}
int findLast(int ql, int qr, ll x) const {
return findLast(1, 1, n, ql, qr, x);
}
int findLast(int v, int l, int r, int ql, int qr, ll x) const {
if (qr < l || r < ql || mn[v] >= x) return 0;
if (l == r) return l;
int m = (l + r) / 2;
int res = 0;
if (qr > m) res = findLast(v * 2 + 1, m + 1, r, ql, qr, x);
if (res) return res;
return findLast(v * 2, l, m, ql, qr, x);
}
};
int main() {
setIO();
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<ll> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
SegTree seg(a);
vector<int> lg(n + 1, 0);
for (int i = 2; i <= n; i++) lg[i] = lg[i / 2] + 1;
int K = lg[n] + 1;
vector<vector<int>> st[3];
for (int k = 0; k < 3; k++) st[k].assign(K, vector<int>(n + 2, n + 1));
for (int i = 1; i <= n; i++) {
for (int k = 1; k <= 3; k++) {
ll need = (a[i] + k - 1) / k;
int last = seg.findLast(1, i, need);
st[k - 1][0][i] = last + 1;
}
}
for (int k = 0; k < 3; k++) {
for (int j = 1; j < K; j++) {
int len = 1 << j;
int half = len >> 1;
for (int i = 1; i + len - 1 <= n; i++) {
st[k][j][i] = min(st[k][j - 1][i], st[k][j - 1][i + half]);
}
}
}
long long ans = 0;
int F[C + 1];
for (int r = 1; r <= n; r++) {
F[0] = r + 1;
for (int c = 1; c <= C; c++) {
int best = F[c - 1];
for (int k = 1; k <= 3 && k <= c; k++) {
int low = max(1, F[c - k] - 1);
int len = r - low + 1;
int j = lg[len];
int cand = min(st[k - 1][j][low], st[k - 1][j][r - (1 << j) + 1]);
best = min(best, cand);
}
F[c] = best;
ans += 1LL * c * (F[c - 1] - F[c]);
if (F[c] == 1) break;
}
}
cout << ans << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const int C = 128;
struct SegTree {
int n;
vector<ll> mn;
SegTree(const vector<ll>& a) {
init(a);
}
void init(const vector<ll>& a) {
n = (int)a.size() - 1;
mn.assign(4 * n + 4, 0);
build(1, 1, n, a);
}
void build(int v, int l, int r, const vector<ll>& a) {
if (l == r) {
mn[v] = a[l];
return;
}
int m = (l + r) / 2;
build(v * 2, l, m, a);
build(v * 2 + 1, m + 1, r, a);
mn[v] = min(mn[v * 2], mn[v * 2 + 1]);
}
int findLast(int ql, int qr, ll x) const {
return findLast(1, 1, n, ql, qr, x);
}
int findLast(int v, int l, int r, int ql, int qr, ll x) const {
if (qr < l || r < ql || mn[v] >= x) return 0;
if (l == r) return l;
int m = (l + r) / 2;
int res = 0;
if (qr > m) res = findLast(v * 2 + 1, m + 1, r, ql, qr, x);
if (res) return res;
return findLast(v * 2, l, m, ql, qr, x);
}
};
int main() {
setIO();
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<ll> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
SegTree seg(a);
vector<int> lg(n + 1, 0);
for (int i = 2; i <= n; i++) lg[i] = lg[i / 2] + 1;
int K = lg[n] + 1;
vector<vector<int>> st[3];
for (int k = 0; k < 3; k++) st[k].assign(K, vector<int>(n + 2, n + 1));
for (int i = 1; i <= n; i++) {
for (int k = 1; k <= 3; k++) {
ll need = (a[i] + k - 1) / k;
int last = seg.findLast(1, i, need);
st[k - 1][0][i] = last + 1;
}
}
for (int k = 0; k < 3; k++) {
for (int j = 1; j < K; j++) {
int len = 1 << j;
int half = len >> 1;
for (int i = 1; i + len - 1 <= n; i++) {
st[k][j][i] = min(st[k][j - 1][i], st[k][j - 1][i + half]);
}
}
}
long long ans = 0;
int F[C + 1];
for (int r = 1; r <= n; r++) {
F[0] = r + 1;
for (int c = 1; c <= C; c++) {
int best = F[c - 1];
for (int k = 1; k <= 3 && k <= c; k++) {
int low = max(1, F[c - k] - 1);
int len = r - low + 1;
int j = lg[len];
int cand = min(st[k - 1][j][low], st[k - 1][j][r - (1 << j) + 1]);
best = min(best, cand);
}
F[c] = best;
ans += 1LL * c * (F[c - 1] - F[c]);
if (F[c] == 1) break;
}
}
cout << ans << '\n';
}
return 0;
}