hdu_4521_小明系列问题——小明序列(LIS)

时间:2021-10-31 20:45:13

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=4521

题意:中文题,不解释

题解:这题就是LIS的加强版,可以用二分的nlogn来做,也可以用线段树的nlogn 做这个带间隔的LIS,具体看代码

#include<stdio.h>
#include<algorithm>
#define root 1,n,1
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define F(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=1e5+7;
int n=N-1,sum[N<<2],a[N],ans,dp[N],d,nn;

void update(int x,int k,int l,int r,int rt){
if(l==r){sum[rt]=k;return;}
int m=(l+r)>>1;
if(x<=m)update(x,k,ls);
else update(x,k,rs);
sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
}

int query(int L,int R,int l,int r,int rt){
if(L>R)return 0;
if(L<=l&&r<=R)return sum[rt];
int m=(l+r)>>1,ret=0;
if(L<=m)ret=max(ret,query(L,R,ls));
if(m<R)ret=max(ret,query(L,R,rs));
return ret;
}

int main(){
while(~scanf("%d%d",&nn,&d)){
F(i,1,nn)scanf("%d",a+i);
F(i,0,(N<<2)-1)sum[i]=0;ans=0;
F(i,1,nn){
if(i>d+1)update(a[i-d-1]+1,dp[i-d-1],root);
dp[i]=query(1,a[i],root)+1;
ans=max(dp[i],ans);
}
printf("%d\n",ans);
}
return 0;
}
/*
状态方程很好想,dp[i] = max(dp[j] + 1)其中a[i] > a[j]

我们把以第i个元素为结尾的最长上升子序列放到线段树对应值为
a[i]的叶子上(有点hash思想,这是为了保证上升这个特性,查询的
时候方便),当然如果此时的i-d<=1就不用插入了,这时候用不到任
何的前置状态。

需要用我们就插入一次,而且每次插入我们都能保证那个点和当前点i
的距离一定大于d(之前已经空了d个位置),到时就直接去线段树上小
于a[i]的区间找最大值就行了
*/