知识铺垫:
upper_bound(f+1,f+n+1,key)-f返回在不降序列f中大于key的第一个元素的下标
lower_bound(f+1,f+n+1,key)-f返回在不降序列f中大于等于key的第一个元素的下标
如果要在非升序列(小于和小于等于)中使用这两个函数,就需要重载运算符
bool cmp(const int& a,const int& b){return a > b;}
- 最长上升子序列问题(Longest Increasing Subsequence,LIS):给定一个长度为n的序列a,求数值单调递增的子序列的长度最长是多少。a的任意子序列b可表示为b={a[k1],a[k2],...,a[kp]},其中k1<k2<...<kp。
- O(n^2)求法:f[i]表示以a[i]为结尾的LIS的长度。则有f[i]=max{f[j]+1},0<j<i,a[j]<a[i],边界:f[0]=0。目标:max(f[i]},1<=i<=n。
- O(nlogn)求法:f[i]表示长度为i的LIS的结尾元素,初始化ans=0,从1到n遍历a,每遇到a[i]>f[ans],就使f[++ans]=a[i]。否则:根据f[]的定义可知,f[]数组是单调递增的,所以可以用二分查找找到p使得f[p]>=a[i],f[p-1]<a[i]令f[p]=a[i](用lower_bound()即可),即找到第一个大于等于a[i]的f[p](lower_bound),令f[p]=a[i](因为a[i]的潜力更大),平均复杂度logn,所以总时间复杂度为nlogn
- 最长下降子序列:查找时找第一个小于等于a[i]的元素(因为此时f[]序列是非升的,所以需要重载运算符,用lower_bound)。
- 最长不下降:a[i]>=f[ans] 则f[++ans]=a[i],否则找到第一个大于a[i]的f[p](upper_bound),
- 最长不上升:a[i]<=f[ans] 则f[++ans]=a[i],否则找到第一个小于a[i]的f[p](也需要重载运算符,用upper_bound),
题意:
给一个长度不超过100000的数列,其中每一个数都是不超过50000的正整数,第一问求这个序列的最长不上升子序列,第二问求将这个序列分为n个不上升子序列时n的最小值。
解题思路:
1. 第一问:直接求最长不上升子序列即可。
2. 第二问:可以用贪心或者有一个Dilworth定理,可知直接求该序列的最长上升子序列的长度即可。
代码:

1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstring> 5 #include <cctype> 6 using namespace std; 7 8 #define res register int 9 inline int read() 10 { 11 int x(0),f(1); char ch; 12 while(!isdigit(ch=getchar())) if(ch=='-') f=-1; 13 while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 14 return f*x; 15 } 16 17 const int N=100000+10; 18 int f1[N],f2[N],a[N],n,ans1,ans2; 19 inline bool cmp(const int&a,const int&b){return a>b;} 20 21 int main() 22 { 23 while(~scanf("%d",&a[++n])); 24 n--; 25 f1[1]=f2[1]=a[1]; ans1=ans2=1; 26 for(res i=2 ; i<=n ; i++) 27 { 28 if(a[i]<=f1[ans1]) 29 f1[++ans1]=a[i]; 30 else 31 *upper_bound(f1+1,f1+ans1+1,a[i],cmp)=a[i]; 32 if(a[i]>f2[ans2]) 33 f2[++ans2]=a[i]; 34 else 35 *lower_bound(f2+1,f2+ans2+1,a[i])=a[i]; 36 } 37 printf("%d %d ",ans1,ans2); 38 return 0; 39 }