首先看几个可以完全放完的方案: 4 3 2 1 5 : (4),(3 4),(2 3 4),(1 2 3 4),(1 2 3 4 5)
3 4 2 5 1 :(3),(3 4),(2 3 4),(2 3 4 5),(1 2 3 4 5)
可见需要找的是一个非降序列和非升序列..他们没有重合的部分..长度之和最大...因为对于非降的..从右进..非升的从左进..又保证了不重合..那么必定从右进的所有数大于等于从左进的所有数...
由于题目没有给出每个数的大小...先离散化..确定每个数在数列中是第几大....找最长非降和最长非升必须用O(nlogn)的算法...这个算法不在累述...在找每个数的最长非升和非降的时候跟新离散化的这个数的最长非降和最长非升..最后..找到每个数k的最长非降+(k~n)的最长非升最大值..这个过程可以O(n)完成..
比如 4 3 2 1 5 ....L[0]记录每个数的长非降..L[1]记录每个数的最长非升...都是从后往前...不如把原数列反过来看..5 1 2 3 4
原值 1 2 3 4 5
离散化后值的顺序 1 2 3 4 5
L[0] 1 2 3 4 1
L[1] 2 2 2 2 1 这其中L[0][i]+max(L[1][k]) < k>i > 的最大值有L[0][3]+L[1][4]=5,L[0][4]+L[1][5]..也就是< (3,2,1),(4,5)>与<(4,3,2,1),(5)>
又比如说3 3 3 3 4 4 3 3 反过来看 3 3 4 4 3 3 3 3...原数列只有两个数值..离散化后就只有两个值了...
原值 3 4
离散化后值的顺序 1 2
L[0] 6 4
L[1] 6 2 这其中L[0][i]+max(L[1][k]) < k>i > 的最大值有L[0][1]+L[1][2]=8,也就是<(3,3,3,3,3,3),(4,4)>最后一个问题..如果已经得到了L[0],L[1]..如何在O(n)的时间内找出L[0][i]+max(L[1][k]) < k>i >的最大值.
对于每个L[0][i]每次要找的是max(L[1][k]) < k>i >,那么不妨将扫描顺序从i最大开始..边更新答案..边更新最长的可用非升...
最后统计时不这么做..用个线段树来维护也行(找区间最大值).我开始是用线段树维护的..写崩了..但一定是可行的...
Program:
#include<iostream>
#include<stack>
#include<queue>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<cmath>
#define ll long long
#define oo 1000000007
#define MAXN 100010
using namespace std;
int n,a[MAXN],num,X[MAXN],b[MAXN],M[MAXN],L[2][MAXN],H[2][MAXN],m[2];
int turn(int x)
{
int l,r,mid;
l=0,r=num+1;
while (r-l>1)
{
mid=(l+r)>>1;
if (X[mid]<=x) l=mid;
else r=mid;
}
return l;
}
void find1()
{
int i,l,r,mid,x;
m[0]=0;
for (i=n;i>=1;i--)
{
x=a[i];
l=0; r=m[0]+1;
while (r-l>1)
{
mid=(l+r)>>1;
if (H[0][mid]<=x) l=mid;
else r=mid;
}
if (r>m[0]) m[0]=r,H[0][r]=a[i];
else H[0][r]=min(H[0][r],a[i]);
x=turn(x);
L[0][x]=max(L[0][x],r);
}
return;
}
void find2()
{
int i,l,r,mid,x;
m[1]=0;
for (i=n;i>=1;i--)
{
x=a[i];
l=0; r=m[1]+1;
while (r-l>1)
{
mid=(l+r)>>1;
if (H[1][mid]>=x) l=mid;
else r=mid;
}
if (r>m[1]) m[1]=r,H[1][r]=a[i];
else H[1][r]=max(H[1][r],a[i]);
x=turn(x);
L[1][x]=max(L[1][x],r);
}
return;
}
int main()
{
int Case,i,ans,MM;
scanf("%d",&Case);
while (Case--)
{
scanf("%d",&n);
for (i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+1+n),b[0]=-oo,num=0;
for (i=1;i<=n;i++)
if (b[i]!=b[i-1]) X[++num]=b[i]; // 由于不知道每个数的范围..相当于离散化了.
memset(L,0,sizeof(L));
find1();
find2();
ans=MM=0;
for (i=num;i>=1;i--)
{
ans=max(ans,L[0][i]+MM);
MM=max(MM,L[1][i]); //更新可用的最长非升
}
printf("%d\n",ans);
}
return 0;
}
/*
5
7
1 2 3 4 5 6 7
5
4 3 2 1 5
5
5 4 1 2 3
8
3 3 3 3 4 4 3 3
5
1 1 1 1 1
*/