题目链接
给出一个长度为 n n n 的序列 a a a 和一个参数 m m m,要求求出一个最长的子序列,满足相邻元素的差大于等于 m m m
本题跟LIS的 O ( n log n ) \operatorname{O}(n\log n) O(nlogn) 做法非常相似,需要用到桶。
首先,我们要将 a [ i ] a[i] a[i] 离散化。
设 f [ i ] f[i] f[i] 表示以 a [ i ] a[i] a[i] 结尾的最长满足条件的子序列的长度, p r e [ i ] pre[i] pre[i] 表示 f [ i ] f[i] f[i] 从 f [ p r e [ i ] ] f[pre[i]] f[pre[i]] 转移过来。
朴素转移: f [ i ] = max ∣ b [ i ] − b [ j ] ≥ m ∣ { f [ j ] } + 1 f[i]=\max\limits_{|b[i]-b[j]\ge m|}\{f[j]\}+1 f[i]=∣b[i]−b[j]≥m∣max{f[j]}+1。
这里的 b [ i ] b[i] b[i] 表示离散化之前的 a [ i ] a[i] a[i]。
我们考虑用桶优化:
设桶 b i n [ j ] bin[j] bin[j] 表示以 j j j 结尾的序列的最长长度, p o s [ j ] pos[j] pos[j] 表示与 b i n [ j ] bin[j] bin[j] 对应的结尾元素的编号。
由于 a [ i ] a[i] a[i] 已经被离散化了,我们需要用二分查找出与 i i i 最接近的 l [ i ] , r [ i ] l[i],r[i] l[i],r[i],满足 1 ≤ l [ i ] ≤ i ≤ r [ i ] ≤ n 1\le l[i] \le i \le r[i] \le n 1≤l[i]≤i≤r[i]≤n,且 a [ i ] − a [ l [ i ] ] ≥ m a[i]-a[l[i]] \ge m a[i]−a[l[i]]≥m 且 a [ r [ i ] ] − a [ i ] ≥ m a[r[i]]-a[i] \ge m a[r[i]]−a[i]≥m。
那么,转移方程就变成了 f [ i ] = max 1 ≤ j ≤ l [ i ] or r [ i ] ≤ j ≤ n { b i n [ j ] } + 1 f[i]=\max\limits_{1 \le j \le l[i] \operatorname{or} r[i] \le j \le n} \{ bin[j] \}+1 f[i]=1≤j≤l[i]orr[i]≤j≤nmax{bin[j]}+1。
由于涉及到区间最值,我们可以用线段树维护,总时间复杂度 O ( n log n ) \operatorname{O}(n \log n) O(nlogn)。
注意每次要更新 b i n bin bin 和 维护 p r e [ i ] pre[i] pre[i]。
#include
#include
#include
#include
#include
using namespace std;
const int Maxn=100000+10,inf=0x3f3f3f3f;
const int Maxm=Maxn<<2;
int maxv[Maxm],p[Maxm];
long long a[Maxn],b[Maxn];
int f[Maxn],pre[Maxn];
int n,m,idcnt;
map <long long,int> id;
inline long long read()
{
long long 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<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
inline void push_up(int k)
{
if(maxv[k<<1]>maxv[k<<1|1])
maxv[k]=maxv[k<<1],p[k]=p[k<<1];
else maxv[k]=maxv[k<<1|1],p[k]=p[k<<1|1];
}
void modify(int k,int l,int r,int pos,int u,int v)
{
if(l==r)
{
maxv[k]=u,p[k]=v;
return;
}
int mid=(l+r)>>1;
if(pos<=mid)modify(k<<1,l,mid,pos,u,v);
else modify(k<<1|1,mid+1,r,pos,u,v);
push_up(k);
}
int query(int k,int l,int r,int x,int y,int &pos)
{
if(x<=l && r<=y)
{
pos=p[k];
return maxv[k];
}
int mid=(l+r)>>1,tot,cnt;
int u,v;tot=cnt=0;
if(x<=mid)tot=query(k<<1,l,mid,x,y,u);
if(mid<y)cnt=query(k<<1|1,mid+1,r,x,y,v);
if(tot>cnt){pos=u;return tot;}
else {pos=v;return cnt;}
}
void calc(int i,int &x,int &y)
{
int l=1,r=i;
if(b[i]-b[l]<m){x=0;goto GG;}
while(l<r)
{
int mid=(l+r)>>1;
++mid;
if(b[i]-b[mid]<m)r=mid-1;
else l=mid;
}
x=l;
GG:
l=i,r=idcnt;
if(b[r]-b[i]<m){y=0;goto FF;}
while(l<r)
{
int mid=(l+r)>>1;
if(b[mid]-b[i]<m)l=mid+1;
else r=mid;
}
y=l;
FF:;
}
void dfs(int x)
{
if(pre[x])dfs(pre[x]);
printf("%d ",x);
}
int main()
{
// freopen("in.txt","r",stdin);
n=read(),m=read();
for(int i=1;i<=n;++i)
a[i]=b[i]=read();
sort(b+1,b+1+n);
for(int i=1;i<=n;++i)
{
b[++idcnt]=b[i];
id[b[idcnt]]=idcnt;
int j=i+1;
while(j<=n && b[i]==b[j])++j;
i=j-1;
}
for(int i=1;i<=n;++i)
{
int x,y,pos;
calc(id[a[i]],x,y);
f[i]=1;
if(x)
{
int tmp=query(1,1,idcnt,1,x,pos);
if(tmp+1>f[i])
f[i]=tmp+1,pre[i]=pos;
}
if(y)
{
int tmp=query(1,1,idcnt,y,idcnt,pos);
if(tmp+1>f[i])
f[i]=tmp+1,pre[i]=pos;
}
modify(1,1,idcnt,id[a[i]],f[i],i);
}
int ans=-1,pos;
for(int i=1;i<=n;++i)
if(f[i]>ans)
ans=f[i],pos=i;
printf("%d\n",ans);
dfs(pos);
putchar('\n');
return 0;
}