luogu5010 HMR的LIS III (dp+线段树)

时间:2023-03-09 04:04:21
luogu5010 HMR的LIS III (dp+线段树)

这个东西和最长上升子序列很像

考虑如果已经知道每个位置为开头的LIS长度和个数 f[i],我可以扫一遍 判断这个个数和K的大小,找到第一个长度=len而且个数<K的,这个位置就是要选的 然后K-=个数,len--,再记下来我这次选的是这个位置(以后还要判断当前位置是否在上一个钦定住的范围内),然后接着做

那这玩意怎么求呢,离散化一下 算出每个数能跳到的下一个数的大小的区间 倒着dp 用线段树记以这个权值为开头的最大长度和个数 优化转移就可以了

 #include<bits/stdc++.h>
#define CLR(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef pair<int,ll> pa;
const int maxn=5e5+; inline ll rd(){
ll x=;char c=getchar();int neg=;
while(c<''||c>''){if(c=='-') neg=-;c=getchar();}
while(c>=''&&c<='') x=x*+c-'',c=getchar();
return x*neg;
} int N,M,Llim,Rlim,L[maxn],R[maxn],a[maxn],tmp[maxn];
int ans[maxn];
ll K;
pa ma[maxn<<],f[maxn]; inline void merge(pa &a,pa b){
if(a.first<b.first) a=b;
else if(a.first==b.first){
a.second+=b.second;
a.second=min(a.second,(ll)1e13+);
}
} inline void update(int p){
ma[p]=make_pair(,);
merge(ma[p],ma[p<<]);merge(ma[p],ma[p<<|]);
} inline void change(int p,int l,int r,int x,pa y){
if(l==r){
merge(ma[p],y);
}else{
int m=l+r>>;
if(x<=m) change(p<<,l,m,x,y);
else change(p<<|,m+,r,x,y);
update(p);
}
} inline void query(int p,int l,int r,int x,int y,pa &z){
if(x>y||x>M) return;
y=min(y,M);
if(x<=l&&r<=y) merge(z,ma[p]);
else{
int m=l+r>>;
if(x<=m) query(p<<,l,m,x,y,z);
if(y>=m+) query(p<<|,m+,r,x,y,z);
}
} int main(){
//freopen("","r",stdin);
int i,j,k;
N=rd(),K=rd(),Llim=rd(),Rlim=rd();
for(i=;i<=N;i++)
a[i]=tmp[i]=rd();
sort(tmp+,tmp+N+);M=unique(tmp+,tmp+N+)-tmp-;
for(i=;i<=N;i++){
L[i]=upper_bound(tmp+,tmp+M+,a[i]+Llim)-tmp;
R[i]=lower_bound(tmp+,tmp+M+,a[i]+Rlim)-tmp-;
a[i]=lower_bound(tmp+,tmp+M+,a[i])-tmp;
// printf("~%d %d %d %d\n",i,a[i],L[i],R[i]);
}
for(i=N;i;i--){
pa re=make_pair(,);
query(,,M,L[i],R[i],re);
if(re.first>=) f[i]=make_pair(re.first+,re.second);
else f[i]=make_pair(,);
change(,,M,a[i],f[i]);
// printf("%d %d %d %d %d %d %d\n",i,L[i],R[i],f[i].first,f[i].second,re.first,re.second);
}
int len=;
for(i=;i<=N;i++)
len=max(len,f[i].first);
printf("%d\n",len);
int lst=-;
for(i=,j=;i<=N;i++){
if(f[i].first!=len||(lst!=-&&(a[i]>R[lst]||a[i]<L[lst]))) continue;
if(f[i].second<K) K-=f[i].second;
else ans[++j]=i,len--,lst=i;
}
for(i=;i<=j;i++)
printf("%d ",ans[i]);
return ;
}