[Luogu 1020 导弹拦截 ]
n*log2(n) 的做法:利用 lower_bound
规律:
1.最长上升子序列:
g[Length] 为长度为 Length 的上升子序列的结尾数值的最小值 , g[x] < g[x+1]
2.最长不下降子序列:
g[Length] 为长度为 Length 的不下降子序列的结尾数值的最小值 , g[x] <= g[x+1]
3.最长下降子序列:
g[Length] 为长度为 Length 的下降子序列的结尾数值的最大值 , g[x] > g[x+1]
4.最长不上升子序列:
g[Length] 为长度为 Length 的不上升子序列结尾数值的最大值 , g[x] >= g[x+1]
{ 即 g[ ] 的单调性与 目标序列一致 }
开始时 ,Length=1,g[1]=a[1],以(4)为例,考虑一个新进的元素 a[i]:
if a[i]<=g[Length] then g[++Length]=a[i];
else in g[ ] to find g[pos] ,g[pos+1]=a[i] | g[pos] >= a[i] >g[Length+1]
即用 a[i]替换掉不升序列 g[ ] 中第一个小于 a[i] 的元素.
实现:二分!(玄学二分毁我三观...)
看代码:

1 //Copyright(C)Sunshine 2 //2018.07.13 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 using namespace std; 7 const int MAXN=1e5+1; 8 int a[MAXN],g[MAXN],ml=0,n=0; 9 int* lowerBound(int* begin,int* end,int val){ 10 //在不升序列中查找 11 int* mid,*l=begin,*r=end; 12 while(l<r){ 13 mid=(r-l)/2+l; 14 if(*mid<val)r=mid;//8 7 6 4 4 4 3 1 15 else l=mid+1; 16 } 17 return l; 18 } 19 int* lower_Bound(int* begin,int* end,int val){ 20 //在不降序列中查找 21 int *mid,*l=begin,*r=end; 22 while(l<r){ 23 mid=(r-l)/2+l; 24 if(*mid>=val)r=mid;//1 2 3 4 6 7 8 9 25 else l=mid+1; 26 } 27 return l; 28 } 29 int main() 30 { 31 while(scanf("%d",&a[0])==1)a[++n]=a[0]; 32 g[ml=1]=a[1]; 33 for(int i=2;i<=n;i++){ 34 if(a[i]<=g[ml])g[++ml]=a[i]; 35 else 36 { 37 int pos=lowerBound(g+1,g+ml+1,a[i])-g; 38 g[pos]=a[i]; 39 } 40 } 41 printf("%d\n",ml); 42 g[ml=1]=a[1]; 43 for(int i=2;i<=n;i++){ 44 if(a[i]>g[ml])g[++ml]=a[i]; 45 else{ 46 int pos=lower_Bound(g+1,g+ml+1,a[i])-g; 47 g[pos]=a[i]; 48 } 49 } 50 printf("%d",ml); 51 return 0; 52 }
//蒟蒻出品,如有错误,欢迎指正.