UVa 1471 - Defense Lines
链接:
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4217
题意:
给一个长度为n(n≤200000)的序列,你的任务是删除一个连续子序列,
使得剩下的序列中有一个长度最大的连续递增子序列。
例如,将序列{5, 3, 4, 9, 2, 8, 6, 7, 1}中的{9, 2, 8}删除,
得到的序列{5, 3, 4, 6, 7, 1}中包含一个长度为4的连续递增子序列{3,4,6,7}。
序列中每个数均为不超过1e9的正整数。
分析:
设L(i)为以第i个元素结尾的最长递增子序列长度,R(j)为以第j个元素开头的最长递增子序列长度,
则最终的答案为最大的 L[i] + R[j] 。枚举j,在一个单调队列里用二分查找快速找到一个i(i<j),
使得 a[i] < a[j] 且 L[i] 最大。则这个单调队列应该满足 a[i] < a[j] 且 L[i] < L[j] (i<j)。
每枚举完一个j,就更新这个单调队列。可以用set来完成上述要求。
代码:
1 #include <cstdio> 2 #include <set> 3 using namespace std; 4 5 const int UP = 2e5 + 5; 6 7 struct TYPE { 8 int a, L; 9 bool operator < (const TYPE& that) const { 10 return a < that.a; 11 } 12 }; 13 14 int a[UP], L[UP], R[UP]; 15 16 int main(){ 17 int T; 18 scanf("%d", &T); 19 while(T--){ 20 int n; 21 scanf("%d", &n); 22 for(int i = 0; i < n; i++) scanf("%d", &a[i]); 23 24 L[0] = 1; //以i结尾的最长连续子序列长度 25 for(int i = 1; i < n; i++) L[i] = a[i-1] < a[i] ? L[i-1] + 1 : 1; 26 27 R[n-1] = 1; //以i开头的最长连续子序列长度 28 for(int i = n - 2; i >= 0; i--) R[i] = a[i] < a[i+1] ? R[i+1] + 1 : 1; 29 30 int ans = 1; 31 set<TYPE> S; 32 S.insert((TYPE){a[0], L[0]}); 33 for(int i = 1; i < n; i++){ 34 bool keep = true; 35 TYPE t = (TYPE){a[i], L[i]}; 36 set<TYPE>::iterator it = S.lower_bound(t); 37 if(it != S.begin()){ 38 int len = R[i] + (--it)->L; 39 ans = max(ans, len); 40 if(t.L <= it->L) keep = false; 41 } 42 if(keep){ 43 S.erase(t); //由于set的特性,当两个TYPE变量的a相同时,这两个变量被认为是相同的。且t.L不会比原来的小 44 S.insert(t); 45 it = ++S.find(t); 46 while(it != S.end() && it->L <= t.L) S.erase(it++); 47 } 48 } 49 printf("%d\n", ans); 50 } 51 return 0; 52 }