将数组反转后,求up[i]表示包括i的最长不降序列,down[i]表示最长不上升序列,取最优。
nlogn的算法:若要求最长上升序列,p[i]表示长度为i的序列的末尾最小值(贪心,最小值更有潜力获得更长的串),很明显p是单调增的,当加入一个新元素时,二分p中最靠右的小于它的值j,更新p[j+1],同时记录up[i]。最长下降同理,p[i]表示长度为i的序列的末尾最大值(贪心,最大值更有潜力获得更长的串),很明显p是单调减的。
因为是由贪心得来的,same[i]表示不上升和不下降序列开始的公共部分。
#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<iomanip>
#include<map>
#include<algorithm>
#include<queue>
#include<set>
#define inf 1000000000
#define pi acos(-1.0)
#define eps 1e-8
#define seed 131
using namespace std;
typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef long long ll;
const int maxn=100005;
int n;
int d[maxn];
int up[maxn];
int down[maxn];
int p[maxn];
int same[maxn];
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=n;i>=1;i--){
scanf("%d",&d[i]);
}
int len=0;
p[0]=0;
for(int i=1;i<=n;i++){
int a=lower_bound(p,p+len+1,d[i])-p;
int b=upper_bound(p,p+len+1,d[i])-p;
same[i]=b-a+1;
if(d[i]>=p[len]){
len++;
p[len]=d[i];
up[i]=len;
}
else{
int k=upper_bound(p,p+len+1,d[i])-p;
k--;
p[k+1]=min(p[k+1],d[i]);
up[i]=k+1;
}
}
len=0;
p[0]=inf;
for(int i=1;i<=n;i++){
int l1=0,r1=len;
int l2=0,r2=len;
while(l1<r1){
int mid=(l1+r1+1)/2;
if(p[mid]>d[i])
l1=mid;
else
r1=mid-1;
}
while(l2<r2){
int mid=(l2+r2+1)/2;
if(p[mid]>=d[i])
l2=mid;
else
r2=mid-1;
}
same[i]=min(same[i],l2-l1+1);
if(d[i]<=p[len]){
len++;
p[len]=d[i];
down[i]=len;
}
else{
int l=0,r=len,mid;
while(l<r){
mid=(l+r+1)/2;
if(p[mid]<d[i])
r=mid-1;
else
l=mid;
}
p[l+1]=d[i];
down[i]=l+1;
}
}
int ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,up[i]+down[i]-same[i]);
cout<<ans<<endl;
}
return 0;
}