ABC339E 题解
题意
给定一个序列
分析
子序列问题很容易想到动态规划。
设
易得
暴力枚举时间复杂度为
考虑优化到
我们发现对于一个
现在要求在
由于
设
初始
枚举到每个
因此查找该区间最大值并加一,以此来更新答案和
此时若更新了
最后输出答案即可。
代码
#include<iostream>
#include<cmath>
using namespace std;
const int N=5e5+10;
const int M=5e5;//N为开数组用的范围,M为实际范围
int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-') w=-1; ch=getchar();}
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar();}
return s*w;
}
int n,d,ans=0,t=1;
int s[N],A[N];//A为原数组,s为目前线段树中存储的最大值
struct nod
{
int u,l,r,s;
}a[N*2];//线段树记得开两倍范围
void pushup(int u)
{
a[u].s=max(a[a[u].l].s,a[a[u].r].s);
}//传递孩子的最大值给父亲
void build(int u,int l,int r)
{
if(l==r)
{
a[u].s=s[l];
return;
}
int mid=(l+r)/2;
a[u].l=++t;
build(t,l,mid);
a[u].r=++t;
build(t,mid+1,r);
pushup(u);
}//建树
void change(int u,int l,int r,int ll,int k)
{
if(l==r&&l==ll)
{
a[u].s=k;
return;
}
int mid=(l+r)/2;
if(ll<=mid)
change(a[u].l,l,mid,ll,k);
else
change(a[u].r,mid+1,r,ll,k);
pushup(u);
}//更改元素并维护最大值
int check(int u,int l,int r,int ll,int rr)
{
if(l>=ll&&r<=rr)
return a[u].s;
int mid=(l+r)/2,ans=0;
if(ll<=mid)
ans=max(ans,check(a[u].l,l,mid,ll,rr));
if(rr>mid)
ans=max(ans,check(a[u].r,mid+1,r,ll,rr));
return ans;
}//查询区间最大值
int main()
{
n=read(),d=read();
for(int i=1;i<=n;i++)
A[i]=read();
build(1,1,M);//建树
for(int i=1;i<=n;i++)
{
int l=max(1,A[i]-d);
int r=min(M,A[i]+d);//注意范围要在1-5e5之间,避免越界
int maxx=check(1,1,M,l,r);//查询最大值
if(ans<maxx+1)
ans=maxx+1;//更新答案
if(s[A[i]]<maxx+1)
{
s[A[i]]=maxx+1;
change(1,1,M,A[i],maxx+1);
}//更新以Ai为结尾的最大长度并维护线段树
}
cout<<ans;
return 0;
}