题意:求长度为n的序列中,间隔至少为d的最长上升子序列的长度。
题解:因为n取值到1e5,到每个元素之前的最长上升子序列用线段树维护,区间内的值是在当前区间内最长上升子序列的最大长度,区间范围是给出的序列中的值确定的,f[i]存从0到a[i]的范围内的最长上升子序列的最大长度,因为间隔要大于等于d,所以第i个元素进行线段树更新节点后,f数组只更新到i-d。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100005;
int n, d, a[N], f[N], tree[N << 2];
int query(int k, int left, int right, int l, int r) {
if (l <= left && right <= r)
return tree[k];
int mid = (left + right) / 2;
if (mid >= r)
return query(k * 2, left, mid, l, r);
if (mid < l)
return query(k * 2 + 1, mid + 1, right, l, r);
return max(query(k * 2, left, mid, l, mid), query(k * 2 + 1, mid + 1, right, mid + 1, r));
}
void modify(int k, int left, int right, int pos, int x) {
if (left == right) {
tree[k] = max(tree[k], x);
return;
}
int mid = (left + right) / 2;
if (pos <= mid)
modify(k * 2, left, mid, pos, x);
else
modify(k * 2 + 1, mid + 1, right, pos, x);
tree[k] = max(tree[k * 2], tree[k * 2 + 1]);
}
int main() {
while (scanf("%d%d", &n, &d) == 2) {
memset(tree, 0, sizeof(tree));
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
int res = 0;
for (int i = 1; i <= n; i++) {
if (a[i] > 0)
res = max(res, f[i] = query(1, 0, N, 0, a[i] - 1) + 1);
else
res = max(res, f[i] = 1);
if (i - d > 0)
modify(1, 0, N, a[i - d], f[i - d]);
}
printf("%d\n", res);
}
return 0;
}