总体思想就是二分的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; }