只能说这题太妙了,场上 tourist 都没过。
首先考虑 的 DP, 表示从 到 的最短路径,转移很简单。
现在要考虑把这个 DP 优化到可以接受的时间复杂度内,考虑有以下性质。
对于 ,如果直接从 跳到 ,那么一定不会比经过区间最小值 跳两次更优。
证明很简单,如果直接从 跳到 ,那么代价就是 。
然而经过 的代价是 。
显然因为对于 ,有 ,所以经过区间最小值更优。
由此可知,每一步的区间最小值一定在两端。
对于一个最小值 ,一定满足他前面和后面的步长一定不会超过 。
证明:
对于一个步长 ,如果每次只跳 的距离,那么每次的花费都不会超过 ,总花费不会超过 。
然而一步跳到的花费是 。
要满足一步跳到更优,就有 ,既 。
有了以上性质我们就只要对于每个 ,考虑以 为最小值转移既可,可以证明这样的时间复杂度是 的。
对于 ,步长上限 ,所以时间复杂度不会超过 。
而对于 ,考虑将将同的 放在一起考虑。
那么对于一个相同的 ,以他们为最小值的区间长度之和是 。
然而最多有 种数,所以时间复杂度也不会超过 。
代码非常好写。
#include <bits/stdc++.h>
using namespace std;
#ifndef LOCAL
void debug(...) {}
#endif
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define per(i, n) for (int i = (n) - 1; ~i; --i)
#define all(v) (v).begin(), (v).end()
using ll = long long;
using vi = vector<int>;
int main() {
#ifndef LOCAL
cin.tie(nullptr)->sync_with_stdio(false);
#endif
constexpr ll inf = 1e18;
int n;
cin >> n;
vi a(n);
vector<ll> f(n, inf);
rep(i ,n) cin >> a[i];
f[0] = 0;
rep(i, n) {
int d = n / a[i];
for (int j = i - 1; j >= 0 && i - j <= d; --j) {
f[i] = min(f[i], f[j] + 1ll * (i - j) * (i - j) * a[i]);
if (a[j] <= a[i]) break;
}
for (int j = i + 1; j < n && j - i <= d; ++j) {
f[j] = min(f[j], f[i] + 1ll * (i - j) * (i - j) * a[i]);
if (a[j] <= a[i]) break;
}
}
rep(i, n) cout << f[i] << ' ';
cout << '\n';
}