Codeforces1468 A. LaIS(dp,单调栈,可持久化线段树)

题意:

在这里插入图片描述
在这里插入图片描述

解法:

在这里插入图片描述

设d[i]表示以a[i]结尾,满足条件的最长子序列.

转移有两种:
1.d[i]=max{d[j]}+1,其中a[j]<=a[i].
2.d[i]=max{d[j]}+2,其中a[j]<=a[i],
同时a[j+1,i-1]中需要有一个a[k],满足a[k]>=a[i]且a[k]>=a[j].

对于转移1,可以在[1,i-1]中权值线段树找(a[j],d[a[j]])的最大值,
其中a[j]<=a[i].

对于转移2,
先单调栈找到i左边第一个>=a[i]的位置k,
然后在[1,k-1]中权值线段树找(a[j],d[a[j]])的最大值.
其中a[j]<=a[i].

由于区间[1,k-1]不是固定的,因此线段树需要可持久化.
可能离线+普通线段树也可以做.
code:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxm=5e5+5;
int stk[maxm],head;
int d[maxm];
int a[maxm];
int n;
//
int rt[maxm],tot;
int lc[maxm*40],rc[maxm*40],ma[maxm*40];
inline void pp(int k){
    ma[k]=max(ma[lc[k]],ma[rc[k]]);
}
void update(int x,int val,int l,int r,int &k,int last){
    k=++tot;
    lc[k]=lc[last];
    rc[k]=rc[last];
    if(l==r){
        ma[k]=val;
        return ;
    }
    int mid=(l+r)/2;
    if(x<=mid)update(x,val,l,mid,lc[k],lc[last]);
    else update(x,val,mid+1,r,rc[k],rc[last]);
    pp(k);
}
int ask(int st,int ed,int l,int r,int k){
    if(!k)return 0;
    if(st<=l&&ed>=r)return ma[k];
    int mid=(l+r)/2;
    int ans=0;
    if(st<=mid)ans=max(ans,ask(st,ed,l,mid,lc[k]));
    if(ed>mid)ans=max(ans,ask(st,ed,mid+1,r,rc[k]));
    return ans;
}
//
void solve(){
    cin>>n;
    //init
    tot=0;
    ma[0]=0;
    //
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    int ans=0;
    head=0;
    for(int i=1;i<=n;i++){
        while(head&&a[stk[head]]<a[i]){
            head--;
        }
        d[i]=1;
        d[i]=max(d[i],ask(1,a[i],1,n,rt[i-1])+1);
        if(head){
            int k=stk[head];
            d[i]=max(d[i],ask(1,a[i],1,n,rt[k-1])+2);
        }
        update(a[i],d[i],1,n,rt[i],rt[i-1]);
        stk[++head]=i;
        ans=max(ans,d[i]);
    }
    cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(0);
    int T;cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值