CF474E 题解

题意简述

给出长度为 n(1n105) 的序列 ai(1ai1015),求 ai 的最长的一个子序列 {b1,b2,,bm},满足 i[1,m),|bibi+1|dd 为给定数且 d109)。

题目分析

裸的 DP 是好想的。类似于经典的 LIS 问题,设 fiai,ai+1,,an 满足题意的子序列的最长长度。那么我们只需要倒着求 fi,对每个 fifi=maxj=i+1,|aiaj|dn{fj+1},直接求是 O(n2) 的。至于具体数列,只需要对每个 i 记录更新它的 j 即可。

考虑优化。注意到 |aiaj|daj[1,aid][ai+d,1015]。那么我们只需要找到满足 jiaj 在上述区间中的最大的 fj 即可。考虑离散化后使用线段树维护,仍是倒着求 fi。对于 fi,先区间查询线段树中值域在 [1,aid][ai+d,1015] 的最大 f 值,然后单点修改 ai 对应的 f 值为 fi。注意维护时顺便记录一下区间里最大的 fj 对应的 j。总时间复杂度 O(nlogn)。具体细节见代码。

代码实现

#include<bits/stdc++.h>
using namespace std;
int n,m,nt[100010],mx=1,mxid=1;
long long d,a[100010],b[100010];
struct node
{
int mx,mxid;//mx:区间最大 f 值,mxid:区间最大 f 值对应的 j
}tr[400010];//线段树结点
node calc(node a,node b)//合并两个结点
{
node c;
if(a.mx>b.mx)
{
c.mx=a.mx;
c.mxid=a.mxid;
}
else
{
c.mx=b.mx;
c.mxid=b.mxid;
}
return c;
}
void pushup(int p)//子结点更新父节点
{
tr[p]=calc(tr[p<<1],tr[p<<1|1]);
}
void build(int p,int l,int r)//建树
{
if(l==r)
{
tr[p].mx=-1;//初始 f 全设为 -1。
tr[p].mxid=0;
return;
}
int mid=l+r>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(p);
}
void change(int p,int x,int v,int id,int l,int r)//单点修改
{
if(l==r)
{
if(v>tr[p].mx)//注意 a_i 可能有重复值,因此是取 max 不是直接赋值
{
tr[p].mx=v;
tr[p].mxid=id;
}
return;
}
int mid=l+r>>1;
if(mid>=x)
change(p<<1,x,v,id,l,mid);//改左子结点
else
change(p<<1|1,x,v,id,mid+1,r);//改右子结点
pushup(p);
}
node query(int p,int L,int R,int l,int r)//区间查询
{
if(r<L||l>R)
return (node){-1,0}; //区间无交集返回无解
if(l>=L&&r<=R)
return tr[p];//区间全包含就直接返回答案
int mid=l+r>>1;
if(mid<L)
return query(p<<1|1,L,R,mid+1,r);//只查询左子结点
else if(mid>=R)
return query(p<<1,L,R,l,mid);//只查询右子结点
else
return calc(query(p<<1,L,R,l,mid),query(p<<1|1,L,R,mid+1,r));//合并子结点的查询答案
}
int main()
{
scanf("%d%lld",&n,&d);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),b[i]=a[i];
sort(b+1,b+n+1);
m=unique(b+1,b+n+1)-b-1;//离散化
build(1,1,m);
for(int i=n;i>=1;i--)
{
node ans=calc(query(1,1,upper_bound(b+1,b+m+1,a[i]-d)-b-1,1,m),query(1,lower_bound(b+1,b+m+1,a[i]+d)-b,m,1,m)); //查询 [1,a_i-d]∪[a_i+d,+∞] 的最大 f
if(ans.mx!=-1)//如果有 f 就从那个 f 更新
{
if(ans.mx>=mx)
mx=ans.mx+1,mxid=i;//更新答案
change(1,lower_bound(b+1,b+m+1,a[i])-b,ans.mx+1,i,1,m);//单点修改
nt[i]=ans.mxid;//记录从哪个 f 过来的
}
else//否则答案为 1
change(1,lower_bound(b+1,b+m+1,a[i])-b,1,i,1,m);//直接修改
}
printf("%d\n",mx);//更新答案
while(mxid)//输出序列
printf("%d ",mxid),mxid=nt[mxid];
return 0;
}
posted @   Hadtsti  阅读(184)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示