AT_abc339_e

传送门。

题意

给你一个长度为 \(N\) 的序列 \(A = (A_1, A_2, \ldots, A_N)\)

求长度为 \(A\) 的子序列的最大长度,使得任意两个相邻项之间的绝对差最多为 \(D\)

分析

\(f[i]\) 为以 \(A\) 中值为 \(i\) 为结尾的子序列的最大长度。则有:

\[f[i] = \max(f[j]) + 1 \]

其中 \(i - d \le j \le i + d\)

代码很容易出来:

for (int i = 1; i <= n; ++i) {
	int x = 0;
	for (int j = max(a[i] - d, 1); j <= min(a[i] + d, mx); ++j) x = min(f[j] + 1, x);
	f[a[i]] = x;
}

\(mx\)\(A\) 中的最大值。

复杂度 \(O(n^2)\)。我们不能接受。

其中这一段:

for (int j = max(a[i] - d, 1); j <= min(a[i] + d, mx); ++j) x = min(f[j] + 1, x);

相当于在找一个区间内的 \(f\) 最大值。

但是,考虑到 \(f[i]\) 会变化。我们使用线段树板子维护这个最大值。

时间复杂度:\(O(n\log n)\)

代码

#include <bits/stdc++.h>
#define int long long
#define ls (p << 1)
#define rs ((p << 1) + 1)
using namespace std;

const int N = 5e5 + 5;
int n, d, f[N], a[N];
struct tree {
	int l, r;
	int mx;
} tr[N * 4];

void pushup(int p) {
	tr[p].mx = max(tr[ls].mx, tr[rs].mx);
}

void build(int l, int r, int p) {
	tr[p].l = l, tr[p].r = r, tr[p].mx = 0;
	if (l == r) { return ; }
	int mid = (l + r) >> 1;
	build(l, mid, ls);
	build(mid + 1, r, rs);
}

void change(int x, int p) {
	int l = tr[p].l, r = tr[p].r;
	if (l == r) {
		tr[p].mx = f[x];
		return ;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) change(x, ls);
	else change(x, rs);
	pushup(p);
}

int que(int L, int R, int p) {
	int l = tr[p].l, r = tr[p].r;
	if (L <= l && r <= R) return tr[p].mx;
	int mid = (l + r) >> 1;
	int res = -1e9;
	if (L <= mid) res = max(res, que(L, R, ls));
	if (R > mid) res = max(res, que(L, R, rs));
	return res;
}

signed main() {
	cin >> n >> d;
	int mx = 0;
	for (int i = 1; i <= n; ++i) cin >> a[i], mx = max(mx, a[i]);
	build(1, mx, 1);
	int ans = -1e9;
	for (int i = 1; i <= n; ++i) {
		int x = a[i];
		f[x] = que(max(1ll, x - d), min(mx, x + d), 1) + 1;
		change(x, 1);
		ans = max(ans, f[x]);
	}
	cout << ans;
	return 0;
}
posted @ 2024-02-03 22:19  lucky_cloud  阅读(21)  评论(0编辑  收藏  举报