此题是属于最长上升子序列的一个变种问题,增加的限制条件为:最长上升子序列相邻的两项在原序列中下标之差要大于d,解决此问题的第一种方法便是对O(nlogn)的LIS算法进行变形,但由于此题的元素取值范围为10^5,我们便可以在求解的过程中用线段树维护:对于j,在1-->j-d-1范围内dp值,线段树的叶子节点的值表示以当前节点为尾且满足上述约束条件的dp值,这样在状态转移时每次查询的时间复杂度由O(n)降为O(logn),因此算法总的时间复杂度成为O(nlogn).
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <utility>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
int max_item[MAXN<<2];
int val[MAXN], dp[MAXN];
int n, d;
void push_up(int rt)
{
max_item[rt] = max(max_item[rt<<1], max_item[rt<<1|1]);
}
void bulid(int l, int r, int rt)
{
max_item[rt] = 0;
if(l == r) return ;
int m = (l + r)>>1;
bulid(l, m, rt<<1);
bulid(m + 1, r, rt<<1|1);
return ;
}
int query(int L, int R, int l, int r, int rt)
{
if(L == l && R == r)
{
return max_item[rt];
}
int m = (l + r)>>1;
if(R <= m)
return query(L, R, l, m, rt<<1);
else if(L > m)
return query(L, R, m + 1, r, rt<<1|1);
else
return max(query(L, m, l, m, rt<<1), query(m + 1, R, m + 1, r, rt<<1|1));
}
void update(int pos, int val, int l, int r, int rt)
{
if(l == r)
{
max_item[rt] = max(max_item[rt], dp[pos]);
return ;
}
int m = (l + r)>>1;
if(val <= m)
update(pos, val, l, m, rt<<1);
else
update(pos, val, m + 1, r, rt<<1|1);
push_up(rt);
return ;
}
int main()
{
//freopen("aa.in", "r", stdin);
//freopen("bb.out", "w", stdout);
int ans, max_e;
while(scanf("%d %d", &n, &d) != EOF)
{
max_e = -1;
for(int i = 1; i <= n; ++i)
{
scanf("%d", &val[i]);
val[i] += 2;
max_e = max(max_e, val[i]);
}
memset(dp, 0, sizeof(dp));
bulid(1, max_e, 1); ans = 0;
for(int i = 1; i <= n; ++i)
{
if(i - d > 1)
update(i - d - 1, val[i-d-1], 1, max_e, 1);
dp[i] = query(1, val[i] - 1, 1, max_e, 1) + 1;
ans = max(ans, dp[i]);
}
printf("%d\n", ans);
}
return 0;
}