ACL Beginner Contest

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;



    
}
posted @ 2020-09-27 10:17  ccsu_zhaobo  阅读(249)  评论(2编辑  收藏  举报