A1929 怪盗基德的滑翔翼(LIS)

求最长上升、下降子序列,取其较大值。

思路一:
\(f(x)\) 表示长度为 \(x\) 的序列对应结尾的最优解。
函数具有单调性,可以二分求解。

#include<bits/stdc++.h>
using namespace std;
const int N=109,inf=1e9;
int dp[N],dp2[N],cnt,cnt2;
//dp 求上升子序列,dp2 求下降子序列。
bool cmp(int a,int b){return a>b;}
int main(){
    int T,n; scanf("%d",&T);
    while(T--){
        scanf("%d",&n),cnt=cnt2=0,dp[0]=-inf,dp2[0]=inf;
        for(int i=1;i<=n;i++) dp[i]=inf,dp2[i]=-inf;
        for(int i=1,a,pos,pos2;i<=n;i++){
            scanf("%d",&a);
            pos=lower_bound(dp,dp+cnt+1,a)-dp;
            pos2=lower_bound(dp2,dp2+cnt2+1,a,cmp)-dp2;
            cnt=max(cnt,pos),dp[pos]=min(dp[pos],a);
            cnt2=max(cnt2,pos2),dp2[pos2]=max(dp2[pos2],a);
        }
        printf("%d\n",max(cnt,cnt2));
    }
    return 0;
}

思路二:
思考暴力转移,是对于每一个位置找前面满足:

  1. 可以传递
  2. 长度最大

的一个位置,而这个需求可以使用树状数组解决,
树状数组的下标是高度,值是序列长度,只需要单点修改前缀 max 即可。
最长下降子序列就用 10000 减一下,转换成最长上升子序列。

#include<bits/stdc++.h>
using namespace std;
const int N=10009,lim=10000;
struct tool{
    int a[N];
    void clear(){
        for(int i=1;i<=lim;i++) a[i]=0;
    }
    int query(int x){
        int ans=0;
        while(x)
            ans=max(ans,a[x]),x-=(x&-x);
        return ans;
    }
    void insert(int x,int k){
        while(x<=lim)
            a[x]=max(k,a[x]),x+=(x&-x);
    }
}A,B;
int main(){
    int T,n; scanf("%d",&T);
    while(T--){
        scanf("%d",&n),A.clear(),B.clear();
        for(int i=1,a;i<=n;i++){
            scanf("%d",&a);
            A.insert(a,A.query(a-1)+1);
            a=10000-a;
            B.insert(a,B.query(a-1)+1);
        }
        printf("%d\n",max(A.query(lim),B.query(lim)));
    }
    return 0;
}

posted @ 2026-02-04 11:41  2025ing  阅读(1)  评论(0)    收藏  举报