LIS的优化
二分优化
在求一个最长不上升自序列中,显然其结尾元素越小,越有利于接其他元素,对答案的贡献也就可能会更高
那么我们可以用low[i]去存长度为i的LIS结尾元素的最小值
因此我们只要维护low数组
对于每一个a[ i ],如果a[ i ] > low [当前最长的LIS长度],就把 a [ i ]接到当前最长的LIS后面,即low [++当前最长的LIS长度] = a [ i ]。
那么,怎么维护 low 数组呢?
对于每一个a [ i ],如果a [ i ]能接到 LIS 后面,就接上去;否则,就用 a [ i ] 取更新 low 数组。具体方法是,在low数组中找到第一个大于等于a [ i ]的元素low [ j ],用a [ i ]去更新 low [ j ]。如果从头到尾扫一遍 low 数组的话,时间复杂度仍是O(n^2)。我们注意到 low 数组内部一定是单调不降的,所有我们可以二分 low 数组,找出第一个大于a[ i ]的元素。二分一次 low 数组的时间复杂度的O(lgn),所以总的时间复杂度是O(nlogn)。
例题
P2782 友好城市
先将所有城市排序,然后发现就是一个最长不下降子序列
但暴力只能拿50pts
1 /* 2 Work by: Suzt_ilymics 3 Knowledge: ?? 4 Time: O(??) 5 */ 6 #include<iostream> 7 #include<cstdio> 8 #include<algorithm> 9 using namespace std; 10 const int MAXN = 2e5+5; 11 struct good{ 12 int n,s; 13 bool operator < (const good &b) const {return n == b.n ? s < b.s : n < b.n; } 14 }a[MAXN]; 15 int n, ans; 16 int f[MAXN]; 17 int main() 18 { 19 scanf("%d", &n); 20 for(int i = 1; i <= n; ++i){ 21 scanf("%d%d", &a[i].n, &a[i].s); 22 } 23 24 sort(a+1, a+n+1); 25 26 for(int i = 1; i <= n; ++i){ 27 f[i] = 1; 28 for(int j = 1; j < i; ++j){ 29 if(a[j].n < a[i].n && a[j].s < a[i].s){ 30 f[i] = max(f[i], f[j] + 1); 31 } 32 } 33 ans = max(ans, f[i]); 34 } 35 36 printf("%d", ans); 37 38 return 0; 39 }
考虑一下上面的优化,因为北岸的城市是排好的,所以low数组里只需存南岸
1 /* 2 Work by: Suzt_ilymics 3 Knowledge: LIS + 二分优化 4 Time: O(nlogn) 5 */ 6 #include<iostream> 7 #include<cstdio> 8 #include<algorithm> 9 #define INF 2100000000 10 using namespace std; 11 const int MAXN = 2e5+5; 12 struct good{ 13 int n,s; 14 bool operator < (const good &b) const {return n == b.n ? s < b.s : n < b.n; } 15 }a[MAXN]; 16 int n, ans; 17 int f[MAXN], low[MAXN]; 18 19 int ef(int r, int k){ 20 int mid, l = 0; 21 while(l <= r){ 22 mid = (l + r) >> 1; 23 if(low[mid] <= k){ 24 l = mid + 1; 25 } 26 else{ 27 r = mid - 1; 28 } 29 } 30 return l; 31 } 32 33 int main() 34 { 35 scanf("%d", &n); 36 for(int i = 1; i <= n; ++i){ 37 scanf("%d%d", &a[i].n, &a[i].s); 38 low[i] = INF; 39 } 40 41 sort(a+1, a+n+1); 42 43 low[1] = a[1].s; 44 ans = 1; 45 46 for(int i = 2; i <= n; ++i){ 47 if(a[i].s > low[ans]){ 48 low[++ans] = a[i].s; 49 } 50 else{ 51 low[ ef(ans, a[i].s) ] = a[i].s; 52 } 53 } 54 55 printf("%d", ans); 56 57 return 0; 58 }