题意
给定一个长度为n的序列a和一个正整数d,要求一个a的子序列b,满足对于b中相邻两个元素的差的绝对值>=d,求最长的子序列b。
分析
首先可以考虑一个最暴力的dp,令dp[i] 表示取了a[i]的最长长度,那么转移就是
dp[i]=max(dp[j])+1(i<j∩abs(a[i]−a[j])≥d)
d
p
[
i
]
=
m
a
x
(
d
p
[
j
]
)
+
1
(
i
<
j
∩
a
b
s
(
a
[
i
]
−
a
[
j
]
)
≥
d
)
然后就是考虑怎么优化啦。
怎么优化呢。
其实优化的方法很简单。从前向后扫描,对于a中的每一个元素,能够接在它后面的元素的取值范围是两个区间(或者一个),可以把这个东西存在线段树里面,然后每一次求dp值,只要查找能够更新到当前值的最大值就好了。得到dp后再更新一次线段时。
感觉上跟lis挺像的[?]
还有一个地方就是要输出这个序列,这样的话只要对于每一个点记录一下它是从哪个地方转移过来的就可以了,然后输出的时候倒着推回去即可。所以说线段树存的不仅是dp值也要存一个编号。
然后题目就完美解决啦。
才怪。其实可以O(n)解的。这个就是用单调栈或者单调队列这样的东西来解决了。
code
#include<bits/stdc++.h>
#define M 300005
#define ll long long
using namespace std;
void read(int &x){
x=0; char c=getchar();
for (;c<48;c=getchar());
for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
}
void read(ll &x){
x=0; char c=getchar();
for (;c<48;c=getchar());
for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
}
struct node{
int res,id;
};
node max(node a,node b){
if (a.res>b.res)return a;
return b;
}
void Max(node &a,node b){
if (a.res<b.res)a=b;
}
struct Tree{
#define ls (p<<1)
#define rs (p<<1|1)
node f[M<<2];
void upd(int l,int r,int x,int y,node val,int p){
if (l==x&&r==y){
Max(f[p],val);
return ;
}
int mid=(l+r)>>1;
if (y<=mid)upd(l,mid,x,y,val,ls);
else if (x>mid)upd(mid+1,r,x,y,val,rs);
else upd(l,mid,x,mid,val,ls),upd(mid+1,r,mid+1,y,val,rs);
}
node qu(int l,int r,int x,int p){
if (l==r)return f[p];
int mid=(l+r)>>1;
if (x<=mid)return max(f[p],qu(l,mid,x,ls));
return max(f[p],qu(mid+1,r,x,rs));
}
}T;
int fr[M],cnt,Q[M];
void pt(int res){
if (res==0)return ;
pt(fr[res]);
Q[++cnt]=res;
}
ll a[M*3],h[M],d;
int ch(int l,int r,ll x){
for (;;){
int mid=(l+r)>>1;
if (a[mid]==x)return mid;
if (a[mid]<x)l=mid+1;else r=mid-1;
}
}
int dp[M],res;
int main(){
// freopen("1.in","r",stdin);
int n,tot=0;
read(n); read(d);
for (int i=1;i<=n;i++){
read(h[i]);
a[++tot]=h[i];
a[++tot]=h[i]+d;
a[++tot]=h[i]-d;
}
int l,r;
sort(a+1,a+tot+1);
tot=unique(a+1,a+tot+1)-a-1;
for (int i=1;i<=n;i++){
l=ch(1,tot,h[i]-d);
r=ch(1,tot,h[i]+d);
node tmp=T.qu(1,tot,ch(1,tot,h[i]),1);
fr[i]=tmp.id;
dp[i]=tmp.res+1;
if (dp[i]>dp[res]){
res=i;
}
T.upd(1,tot,1,l,(node){dp[i],i},1);
T.upd(1,tot,r,tot,(node){dp[i],i},1);
}
printf("%d\n",dp[res]);
pt(res);
for (int i=1;i<cnt;i++)printf("%d ",Q[i]); printf("%d\n",Q[cnt]);
return 0;
}