Official

F - Athletic Editorial by en_translator


The height of scaffold decreases monotonically as he moves. Thus, we can compute the maximum number of moves starting from each scaffold in ascending order of its height with a Dynamic Programming (DP).

Let \(dp_i\) be the maximum number of moves starting from scaffold \(i\). The answer to the original problem is the maximum value of \(dp\). Also, define a sequence \(p\) so that \(p_{H_i}=i\).

Then \(dp\) can be found as follows.

  • Let \(dp=(0,0,\ldots,0)\).
  • For \(i=1,2,\ldots,N\), do the following:
    • For each \(j\) with \(|p_i-j| \leq R, H_j \leq i-D\), let \(dp_{p_i} \leftarrow \max(dp_{p_i},dp_j+1)\).

If you enumerate \(j\) for each \(i\), the time complexity will be \(\mathrm{O}(ND)\) or \(\mathrm{O}(NR)\), which hardly fits in the execution time limit.

Let us reorganize the enumeration of \(j\) for each \(i\), namely the \(H_j \leq i-D\) part. Then the procedure above can be rephrased as follows:

  • Initialize with \(dp=(0,0,\ldots,0), S=(-\infty,-\infty,\ldots,-\infty)\).
  • For \(i=1,2,\ldots,N\) in order, do the following:
    • If \(D+1 \leq i\), set \(S_{p_{i-D}} \leftarrow dp_{p_{i-D}}\).
    • Set \(dp_{p_i} \leftarrow \max(dp_{p_i},M+1)\), where \(l=\max(1,p_i-R),r=\min(N,p_i+R),M=\max(S_{l},S_{l+1},\ldots,S_{r})\).

In this procedure, the bottleneck is finding the maximum value of a segment of \(S\), which can be optimized by managing \(S\) in a segment-max segment tree. Thus, the problem can be solved in a total of \(\mathrm{O}(N \log N)\) time.

Sample code (C++)

#include <bits/stdc++.h>
#include <atcoder/segtree>
using namespace std;
int op(int a, int b) {
    return max(a, b);
}
int e() {
    return -1e9;
}
int main() {
    int n, d, r;
    cin >> n >> d >> r;
    vector<int> h(n);
    for (auto &e : h)
        cin >> e;
    for (auto &e : h)
        e--;
    vector<int> p(n);
    for (int i = 0; i < n; i++)
        p[h[i]] = i;
    vector<int> dp(n, 0);
    atcoder::segtree<int, op, e> seg(n);
    for (int i = 0; i < n; ++i) {
        if (i - d >= 0) {
            seg.set(p[i - d], dp[i - d]);
        }
        int mx = seg.prod(max(0, p[i] - r), min(n, p[i] + r + 1));
        dp[i] = max(dp[i], mx + 1);
    }
    cout << *max_element(dp.begin(), dp.end()) << '\n';
}

posted:
last update: