大体题意:
给你一个长度为n(n < 2e5)的序列,你的任务是删除一个连续的子序列,使得剩下的序列中有一个长度最大的连续递增子序列。求最大序列长度?
思路:
因为要删除一个连续的子序列,所以会分成左右两部分之和的形式,我们枚举右边一部分的起点位置i,快速的找到左边一个合适的位置j,使得a[j] < a[i] 并且 g[j]尽量大。
(注: g[i]表示以i 位置结束的最长上升连续子序列的长度,f[i]表示以i位置开始的最长上升连续子序列的长度)
这样我们可以利用set 来维护当前合法的左边位置的值。
什么是合法的? 必须是a[i] > a[j] 并且 g[i] > g[j], 这样我们可以按照a[i]排序,那么g[i]也一定是升序的,
这样我们加入一个 就删除不合法的。
利用set里的二分查找lower_bound 便很快的查到合法的位置了。
详细见代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#define Max(a,b)((a)<(b)?(b):(a))
using namespace std;
const int maxn = 2e5 + 10;
int T, n;
int g[maxn], f[maxn], a[maxn];
struct Node{
int id;
int len;
Node(int id = 0,int len = 0):id(id),len(len){}
bool operator < (const Node& rhs) const {
return a[id] < a[rhs.id] || (a[id] == a[rhs.id ] && len < rhs.len);
}
bool operator == (const Node & rhs) const {
return id == rhs.id && len == rhs.len;
}
};
set<Node>s;
set<Node>::iterator it,it2,it3;
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
s.clear();
for (int i = 1; i <= n; ++i)scanf("%d",a+i);
g[1] = 1;
for (int i = 2; i <= n; ++i){
if (a[i] > a[i-1]){
g[i] = g[i-1] + 1;
}
else g[i] = 1;
}
f[n] = 1;
for (int i = n-1; i >= 1; --i){
if (a[i] < a[i+1] ) f[i] = f[i+1] + 1;
else f[i] = 1;
}
int ans = 1;
for (int i = 1; i <= n; ++i)ans = Max(ans,f[i]);
s.insert(Node(1,1));
for (int i = 2; i <= n; ++i){
Node v = Node(i,g[i]);
it = s.lower_bound(v);
if (it != s.begin()){
--it;
Node u = *it;
ans = Max(ans,u.len + f[i]);
++it;
}
--it;
s.insert(v);
it = s.find(v);
it2 = it;
if (it2 != s.begin()){
--it2;
Node u = *it2;
if (a[u.id] <= a[v.id]){
if (u.len>= v.len) {
s.erase(it);
continue;
}
}
}
it2 = it;
++it2;
for ( ;it2 != s.end(); ){
Node u = *it2;
if (a[u.id] >= a[v.id]){
if (u.len <= v.len) it2 = s.erase(it2);
else break;;
}
}
}
printf("%d\n",ans);
}
return 0;
}
/**
2
9
5 3 4 9 2 8 6 7 1
7
1 2 3 10 4 5 6
**/