传送门:题目
题意:
给一个由n个数组成的序列,然后从左到右取一些数组成一个新序列,这个新序列满足一些条件:
- 新序列是递增的
- 原序列取得过程中,相邻两个元素的位置间隔至少为d
- 原序列的个数尽可能多
题解:
第一个条件明显是LIS,第二个题目告诉我们这道题是LIS的变形,说是变形,其实就改了一点,我们在求普通LIS的时候,dp[i]=dp[i-1]+1
,这道题:dp[i]=dp[i-d-1]+1
就好了。然后第三个条件,就是把所有满足条件的LIS都求出来,然后取个最大值就好了。
数据范围
105
10
5
,我们在求dp的时候不能
n2
n
2
搜索了,要加个线段树,单点更新,区间最值 。
AC代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define debug(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 110000;
int dp[maxn], a[maxn];
/******************线段树模板**********************/
int SegTree[maxn * 4];
void BuildTree(int l, int r, int rt) {//建树,lr是总区间,rt是根结点一般为1
if (l == r) {
SegTree[rt] = 0; //初始化叶节点
return ;
}
int m = (l + r) >> 1;
BuildTree(l, m, rt << 1);
BuildTree(m + 1, r, rt << 1 | 1);
SegTree[rt] = SegTree[rt << 1] + SegTree[rt << 1 | 1];
}
int Query(int L, int R, int l, int r, int rt) {//区间查询,LR是查询区间,lr是总区间,rt是根结点一般为1
if (L > R)//注意这里,我第一开始没加,一直MLE
return 0;
if (l >= L && r <= R)
return SegTree[rt];
int m = (l + r) >> 1;
int ans1 = 0, ans2 = 0;
if (L <= m)
ans1 = Query(L, R, l, m, rt << 1);
if (R > m)
ans2 = Query(L, R, m + 1, r, rt << 1 | 1);
return max(ans1, ans2);
}
void Update(int point, int value, int l, int r, int rt) {//单点更新,把point点的值改为value,lr是总区间,rt是根结点一般为1
if (l == r) {
SegTree[rt] = max(value, SegTree[rt]);
return;
}
int m = (l + r) >> 1;
if (point <= m)
Update(point, value, l, m, rt << 1);
else
Update(point, value, m + 1, r, rt << 1 | 1);
SegTree[rt] = max(SegTree[rt << 1] , SegTree[rt << 1 | 1]);
}
/******************线段树模板**********************/
int main(void) {
int n, d;
while (cin >> n >> d) {
memset(dp, 0, sizeof dp);
int mmax = 0, ans = 0;
for (int i = 1; i <= n; i++)
cin >> a[i], ++a[i], mmax = max(mmax, a[i]);
BuildTree(1, mmax, 1);
for (int i = 1; i <= n; i++) {
if (i - d - 1 >= 1)
Update(a[i - d - 1], dp[i - d - 1], 1, mmax, 1);
dp[i] = Query(1, a[i] - 1, 1, mmax, 1) + 1;
ans = max(dp[i], ans);
}
cout << ans << endl;
}
return 0;
}