首先很快想到一个 O ( n 2 ) O(n^2) O(n2) 的 DP:
状态:设 d p i dp_i dpi 为以 a i a_i ai 为结尾的子序列的最大长度。
转移:若 ∣ a i − a j ≤ d ∣ \left|a_i-a_j\le d\right| ∣ai−aj≤d∣, d p i = max ( d p i , d p j + 1 ) dp_i=\max (dp_i,dp_j+1) dpi=max(dpi,dpj+1)。
答案: max i = 1 n d p i \max\limits_{i=1}^{n}dp_i i=1maxndpi。
但是 O ( n 2 ) O(n^2) O(n2) 的复杂度接受不了,考虑线段树优化 DP。使用权值线段树,下标为 a i a_i ai 的值。查找 a i − d ∼ a i + d a_i - d \sim a_i + d ai−d∼ai+d 之间 d p dp dp 的最大值,然后更新即可。
时间复杂度 O ( n log V ) O(n\log V) O(nlogV), V V V 为值域。
// by zhujiangyuan 2024/4/16
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e5;
int n, d, a[N + 10];
struct Seg {
int dat;
#define dat(p) t[p].dat
#define ls p << 1
#define rs p << 1 | 1
}t[N * 4];
int query (int p, int l, int r, int L, int R) {
if (L <= l && r <= R) return dat(p);
int mid = (l + r) >> 1, res = 0;
if (L <= mid) res = max (res, query (ls, l, mid, L, R));
if (mid < R) res = max (res, query (rs, mid + 1, r, L, R));
return res;
}
void modify (int p, int l, int r, int pos, int val) {
if (l == r) { dat(p) = max (dat(p), val); return ;}
int mid = (l + r) >> 1;
if (pos <= mid) modify (ls, l, mid, pos, val);
else modify (rs, mid + 1, r, pos, val);
dat(p) = max (dat(ls), dat(rs));
}
int main()
{
scanf("%d %d", &n, &d);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = n; i >= 1; i--) {
int maxx = query (1, 1, N, max (a[i] - d, 1), min (a[i] + d, N));
modify (1, 1, N, a[i], maxx + 1);
}
printf("%d", query(1, 1, N, 1, N));
return 0;
}