Official

E - Smooth Subsequence Editorial by en_translator


Let \(dp_{i,j}\) be the maximum length of a subsequence of \((A_1, A_2, \dots, A_i)\) whose adjacent terms differ at most by \(D\) and last term is \(j\).

Then it holds that

\( dp_{i,j} = \begin{cases} \displaystyle\max_{k \in [A_i-D, A_i + D]} dp_{i - 1, k} + 1 & (j = A_i) \\ dp_{i-1,j} & (j \neq A_i). \end{cases}\)

If you follow this equation to fill the DP (Dynamic Programming) array, you can obtain the correct answer, but the time and spacial complexity would be \(\Theta(N^2)\).

Instead, we use the fact that \(dp_{i - 1,j}\) and \(dp_{i,j}\) coincides for almost all \(j\).

Instead of explicitly maintaining \(dp_{i,j}\) for each \((i,j)\), one can reuse an array, so that the DP above can be evaluated by maintaining a one-dimensional array \(dp\) and, for \(i = 1, 2, \ldots, N\) in this order, replacing \(dp_j\) with \(\displaystyle\max_{k \in [A_i-D, A_i + D]} dp_k + 1\).

This way, the spacial complexity is reduced to \(O(N)\). While the time complexity is still \(\Theta(N^2)\), one can use a segment tree that allows segment-max and element-update to reduce it to \(O(N \log N)\).

Sample code

#include <bits/stdc++.h>
using namespace std;

#include <atcoder/all>

int op(int a, int b) { return max(a, b); }
int e() { return 0; }

const int A = 500010;

int main() {
    int n, d;
    cin >> n >> d;
    vector<int> a(n);
    for (int &e : a) cin >> e;
    atcoder::segtree<int, op, e> seg(A);
    for (int e : a) {
        int l = max(0, e - d);
        int r = min(A - 1, e + d);
        int mx = seg.prod(l, r + 1);
        seg.set(e, mx + 1);
    }
    cout << seg.all_prod() << '\n';
}

posted:
last update: