ACL Beginner Contest
D - Flat Subsequence
题意:
给你n个数, 让你选择一个最长的子序列a 且 a[i + 1] - a[i]的绝对值小于等于k问最长的子序列长度为多少?
题解:
如果写成dp复杂度为(n^2)那肯定过不了的, 如果把dp方程写出来
for (int i = 1; i <= n; i++) {
for (int j = 1; j < i; j++) {
if (abs(a[i] - a[j]) <= k) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
你会发现第二次for循环中每次找的都是 前i个的最大值。
区间最大值?
是不是可以用线段树维护呀?
那怎么确定是否满足(abs(a[i] - a[j]) <= k)呢?
这个我们可以把线段树建立成权值线段树, 这样每次查询只产([max(a[k] - k, 0) , a[k] + k])的值就好了。
权值线段就是维护当前值为a[i]的dp值。
因为相同的权值只会保留 比她之前更大或者相等的dp值,所有按照权值来存是ok的。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 6e5 + 7;
int n, a[N], k, tree[4 * N];
#define m (l + r) / 2
#define lson 2 * node
#define rson 2 * node + 1
void update(int pos, int v, int l, int r, int node) {
if (l == r) {
tree[node] = v;
return;
}
if (pos <= m) update(pos, v, l, m, lson);
else update(pos, v, m + 1, r, rson);
tree[node] = max(tree[lson], tree[rson]);
}
int query(int ql, int qr, int l, int r, int node) {
if (ql <= l && qr >= r) {
return tree[node];
}
int ans = 0;
if (ql <= m) ans = max(ans, query(ql, qr, l, m, lson));
if (qr > m) ans = max(ans, query(ql, qr, m + 1, r, rson));
return ans;
}
int main() {
scanf("%d %d", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
int ans = 0;
for (int i = 1; i <= n; i++) {
int res = query(max(a[i] - k, 0), a[i] + k, 0, N, 1) + 1;
ans = max(ans, res);
update(a[i], res, 0, N, 1);
}
cout << ans << endl;
}