题解:P15278 [MCO 2025] Subsequence

· · 题解

笑点解析:由于取整问题 WA 了 12 次。

但也至少是我第一次独立切蓝。

思路

f_i 表示以 a_i 为结尾的最长有效子序列长度,很明显有转移:

f_i=\max_{j=1}^{i-1}f_j+1

其中满足 a_i\times a_j\le x。时间复杂度 O(n^2),需要优化。

考虑将状态变为:f_p 表示以 p 为结尾的最长有效子序列长度,则有转移:

f_{a_i}=\max f_j+1

其中 j\times a_i\le x。不难发现这个等价于 j\le\ge\frac{x}{a_i},所以假设我们把 \left[1,i-1\right]a_i 排好序后,这个在区间 \left[1,i-1\right] 中出现的 j 的下标 pos 一定在一段区间中,具体在哪段区间分讨一下就可以了。

你刚刚提到了区间是吧!那咱们就可以用线段树维护 f_j,即可做到 O(n\log n) 的时间复杂度。

## 代码 ``` const int N=1e5+8; int n,x,ans,m; int a[N],b[N]; struct seg_tree { int mx; }t[N<<2]; void update(int o,int l,int r,int idx,int val) { if(l==r&&l==idx) { t[o].mx=max(t[o].mx,val); return ; } int mid=(l+r)>>1; if(mid>=idx) update(ls,l,mid,idx,val); else update(rs,mid+1,r,idx,val); t[o].mx=max(t[ls].mx,t[rs].mx); } int query(int o,int l,int r,int L,int R) { if(L>R) return 0; if(L<=l&&r<=R) return t[o].mx; int mid=(l+r)>>1; int res=0; if(mid>=L) res=max(res,query(ls,l,mid,L,R)); if(mid<R) res=max(res,query(rs,mid+1,r,L,R)); return res; } signed main() { n=read();x=read(); for(int i=1;i<=n;i++) a[i]=read(),b[i]=a[i]; sort(b+1,b+n+1); m=unique(b+1,b+n+1)-b-1; for(int i=1;i<=n;i++) { int tmp,f=0; if(a[i]>0) { tmp=upper_bound(b+1,b+m+1,x/a[i]-(x<0&&x%a[i]?1:0))-b-1; f=query(1,1,m,1,tmp)+1; } else { tmp=lower_bound(b+1,b+m+1,x/a[i]+(x<0&&x%a[i]?1:0))-b; f=query(1,1,m,tmp,m)+1; } int cur=lower_bound(b+1,b+m+1,a[i])-b; update(1,1,m,cur,f); ans=max(ans,f); } cout<<ans; return 0; } ```