总体思想就是二分的LIS+标记重复的数
从后往前遍历数组:
每运行一个数的时候,可以找出从这个数到数组尾的最长不上升子序列的长度num和最长不下降子序列的长度nums。
然后用sum标记这个数在最长不上升子序列中存在的个数,用sums标记这个数在最长不下降子序列中存在的个数。
然后如果 deque数组中第一个放入的数是这个数的话, deque的长度就等于num+nums-min(sum,sums);
遍历一边之后,找出最大的数就可以了。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
struct list
{
int i;
int x;
int y;
}node[200001];
int num[200005];
int len[200005];
int sum[200005];
int lens[200005];
int sums[200005];
int cmp1(list a,list b)
{
return a.x<b.x;
}
int cmp2(list a,list b)
{
return a.i<b.i;
}
int main()
{
int T,n,i;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%d",&node[i].x);
node[i].i=i;
}
sort(node,node+n,cmp1);
int leap=2;
node[0].y=1;
for(i=1;i<n;i++)
{
if(node[i].x==node[i-1].x)node[i].y=node[i-1].y;
else node[i].y=leap++;
}
sort(node,node+n,cmp2);
for(i=0;i<n;i++)num[i]=node[n-i-1].y;
int l,r;
l=r=0;
len[r++]=num[0];
memset(sum,0,sizeof(sum));
sum[num[0]]++;
int ls,rs;
ls=rs=0;
lens[rs++]=num[0];
memset(sums,0,sizeof(sums));
sums[num[0]]++;
int maxnn;
maxnn=1;
for(i=1;i<n;i++)
{
int l1,l2,mid;
if(num[i]>=len[r-1])
{
len[r++]=num[i];
sum[num[i]]++;
l1=r;
}
else
{
l=0;
int rr=r;
while(l<r)
{
mid=(l+r)/2;
if(len[mid]<=num[i])l=mid+1;
else if(len[mid]>num[i]&&(mid==0||len[mid-1]<=num[i]))break;
else r=mid;
}
sum[len[mid]]--;
sum[num[i]]++;
len[mid]=num[i];
l1=mid+1;
r=rr;
}
if(num[i]<=lens[rs-1])
{
lens[rs++]=num[i];
sums[num[i]]++;
l2=rs;
}
else
{
ls=0;
int rr=rs;
while(ls<rs)
{
mid=(ls+rs)/2;
if(lens[mid]>=num[i])ls=mid+1;
else if(lens[mid]<num[i]&&(mid==0||lens[mid-1]>=num[i]))break;
else rs=mid;
}
sums[lens[mid]]--;
sums[num[i]]++;
lens[mid]=num[i];
l2=mid+1;
rs=rr;
}
if(maxnn<(l1+l2-min(sum[num[i]],sums[num[i]])))maxnn=l1+l2-min(sum[num[i]],sums[num[i]]);
}
cout<<maxnn<<endl;
}
return 0;
}