CF207B2 Military Trainings
考虑朴素的 DP 方程,设 $f_i$ 表示从 $1 \sim i$ 需要的最小步数。则:$$f_i = \min_{i-a_i < j < i} f_j+1$$
将这个 DP 作 $n$ 轮即可。复杂度 $O(n^3)$
对于一个 $i$,考虑优化选 $j$ 的过程。看上去每次选的 $j$ 都是一样的,发现对于可以到达的 $j$, $j - a_j$ 最小的 $j$ 是最优的。记 $i$ 可到达的其他所有点为 $k$。因为 $k$ 能到达的点 $j$ 都能到达,而 $j$ 可以到达 $k$ 不能到达的点。所以我们可以把选点过程记录下来。具体来说,使用 ST 表预处理出 $l,r$ 中 $k - a_k$ 最小的点。每次 $O(1)$ 查询,总复杂度 $O(n^2)$。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e6 + 500;
int n;
int a[N];
int lg[N];
int st[N][30];
int query(int l, int r) {
int j = lg[r - l + 1];
int sp = 1 << j;
bool tag = a[st[l][j]] <= a[st[r - sp + 1][j]];
return tag ? st[l][j] : st[r - sp + 1][j];
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]), a[i + n] = a[i];
for(int i = 1; i <= n << 1; ++i) a[i] = max(1, i - a[i]);
lg[0] = -1;
for(int i = 1; i <= n << 1; ++i) lg[i] = lg[i >> 1] + 1;
for(int i = 1; i <= n << 1; ++i) st[i][0] = i;
for(int j = 1; j <= lg[n << 1] + 1; ++j) {
for(int i = 1; i <= n << 1; ++i) {
int sp = 1 << (j - 1);
bool tag = a[st[i][j - 1]] <= a[st[i + sp][j - 1]];
st[i][j] = tag ? st[i][j - 1] : st[i + sp][j - 1];
}
}
int ans = 0;
for(int k = 1; k <= n; ++k) {
int i = k + n - 1, j = a[k + n - 1];
ans++;
while(k < j) {
int t = query(j, i - 1);
i = t, j = a[t];
//cerr << max(i, i - n) << " " << j << endl;
ans++;
}
}
printf("%d", ans);
return 0;
}

浙公网安备 33010602011771号