abc339 E - Smooth Subsequence
思路:我们很容想到一个
n
n
n方的的状态转移方程,即对于每个i,我们去枚举
1
1
1到
i
−
1
i-1
i−1的状态,即
d
p
[
i
]
=
m
a
x
(
d
p
[
i
]
,
d
p
[
j
]
+
1
)
;
dp[i]=max(dp[i],dp[j]+1);
dp[i]=max(dp[i],dp[j]+1);
但是由于时间的限制,我们无法使用n方的dp去完成这道题目,考虑进行优化,因为对于状态
i
i
i,其状态只能由前面点的状态转移过来,我们同样可以换一种说法,对于值
a
[
i
]
a[i]
a[i],其状态只能由
(
a
[
i
]
−
d
,
a
[
i
]
+
d
)
(a[i]-d,a[i]+d)
(a[i]−d,a[i]+d)这个范围内的数转移过来,我们想要长度最大,那么我们把值域当作下标进行维护,答案即为范围内的最大值,计算完当前状态后,我们再把当前的状态放入所谓的值域中。
对于上述操作,将值域看作下标后,其本质就是区间查询最大值,然后单点进行修改,因此线段树完全可以代替第二层循环,将时间复杂度降低到log级别。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 5> ar;
int mod = 1e9+7;
// const int maxv = 4e6 + 5;
// #define endl "\n"
int dp[N];
int a[N];
struct node
{
int l,r,val;
#define l(x) tr[x].l
#define r(x) tr[x].r
#define val(x) tr[x].val
}tr[N*4];
void update(int p)
{
val(p)=max(val(p*2),val(p*2+1));
}
void build(int p,int l,int r)
{
if(l==r){
tr[p]={l,r,0};
return ;
}
l(p)=l,r(p)=r;
int mid=(l+r)/2;
build(p*2,l,mid),build(p*2+1,mid+1,r);
}
void change(int p,int pos,int x)
{
if(l(p)==r(p)){
val(p)=x;
return ;
}
int mid=(l(p)+r(p))/2;
if(pos<=mid) change(p*2,pos,x);
else change(p*2+1,pos,x);
update(p);
}
int query(int p,int l,int r)
{
if(l<=l(p)&&r(p)<=r) return val(p);
int mid=(l(p)+r(p))/2;
int res=0;
if(l<=mid) res=max(res,query(p*2,l,r));
if(r>mid) res=max(res,query(p*2+1,l,r));
return res;
}
void solve()
{
int n,d;
cin>>n>>d;
for(int i=1;i<=n;i++){
cin>>a[i];
// dp[i]=1;
}
build(1,1,N);
for(int i=1;i<=n;i++){
int cur=query(1,max(0,a[i]-d),min(N,a[i]+d));
dp[i]=cur+1;
change(1,a[i],dp[i]);
}
cout<<query(1,1,N)<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
t=1;
// cin>>t;
while(t--){
solve();
}
system("pause");
return 0;
}