题意:
解法:
设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;
}