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; }
本文作者:lucky_cloud
本文链接:https://www.cnblogs.com/luckycloud/p/18005312
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步