题目大意
%
给定一个长度为
n
n
n 的序列
A
A
A,定义一个序列的连续上升子序列为这个序列中数值严格递增的一段连续子序列,你需要删除其中连续的一段,使得剩下的元素构成的连续上升子序列尽可能长,你需要输出这个最长的长度。
%
数据范围
n
∈
(
0
,
2
⋅
1
0
5
]
∩
N
,
a
i
∈
(
0
,
1
0
9
]
∩
N
∗
n\in(0, 2\cdot10^5]∩\N,a_i\in(0,10^9]∩\N^*
n∈(0,2⋅105]∩N,ai∈(0,109]∩N∗
题解
%
考虑最朴素的算法,先枚举删掉的一段的起始位置
i
i
i,结束位置
j
j
j,然后计算出最长的连续上升子序列,总时间复杂度为
Θ
(
n
3
)
\Theta(n^3)
Θ(n3)。
考虑优化每次计算最长的连续上升子序列的过程,可以发现,如果能通过删除区间
[
i
,
j
]
[i,j]
[i,j] 得到更长的最长的连续上升子序列,必然有
a
i
<
a
j
a_i<a_j
ai<aj,因而我们可以预处理出从每个位置开始向前的最长连续上升子序列
p
i
p_i
pi 和向后的连续上升子序列
f
i
f_i
fi。令
P
=
max
i
=
1
n
p
i
,
F
=
max
i
=
1
n
f
i
P=\max_{i=1}^n p_i,F=\max_{i=1}^n f_i
P=maxi=1npi,F=maxi=1nfi,则删除
[
i
,
j
]
[i,j]
[i,j] 后的答案不会大于
max
(
p
r
e
i
+
s
u
f
j
,
P
,
F
)
\max(pre_i+suf_j,P,F)
max(prei+sufj,P,F)。如此,程序的时间复杂度被优化到了
Θ
(
n
2
)
\Theta(n^2)
Θ(n2)。
这样的时间复杂度仍然无法通过本题,我们考虑能否只枚举
i
i
i。对于每个
i
i
i ,我们的目标是找到一个
j
j
j,满足
a
j
>
a
i
a_j>a_i
aj>ai,且
s
u
f
j
suf_j
sufj 尽可能大。
如果我们用
t
s
t_s
ts 表示所有满足
a
k
=
s
a_k=s
ak=s 的
k
k
k 对应的最大的
s
u
f
k
suf_k
sufk,则我们可以从后往前枚举
i
i
i,每次统计满足
s
∈
[
a
i
,
+
∞
]
s\in [a_i,+\infty]
s∈[ai,+∞] 的最大的
t
s
t_s
ts。可以发现,如果用线段树来维护
t
t
t,每次单点修改,区间查询最大值,就可以做到
Θ
(
n
log
2
n
)
\Theta(n\log_2 n)
Θ(nlog2n) 的时间复杂度。
考虑到
a
i
a_i
ai 的范围过大,因而需要离散化。
代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 200010
int T,n,a[maxn],pre[maxn],fix[maxn],t[maxn];
struct node{
int l,r,maxx;
node *left,*right;
node(int tl=0,int tr=0):l(tl),r(tr),left(NULL),right(NULL),maxx(0){
if(tl==tr) return;
int mid=(tl+tr)>>1;
left=new node(tl,mid);
right=new node(mid+1,tr);
}
void change(int x,int d){
if(l==r) return (void)(maxx=d);
if(x<=left->r) left->change(x,d);
else right->change(x,d);
maxx=max(left->maxx,right->maxx);
}
int ask(int tl,int tr){
if(tl>tr) return 0;
if(l==tl&&r==tr) return maxx;
if(tr<=left->r) return left->ask(tl,tr);
else if(tl>=right->l) return right->ask(tl,tr);
return max(left->ask(tl,left->r),right->ask(right->l,tr));
}
};
int arr[maxn];
int main(){
scanf("%d",&T);
while(T--){
memset(arr,0,sizeof arr);
memset(pre,0,sizeof pre);
memset(fix,0,sizeof fix);
int ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
t[i]=a[i];
} sort(t+1,t+1+n);
int m=unique(t+1,t+1+n)-(t+1);
for(int i=1;i<=n;i++)
a[i]=lower_bound(t+1,t+1+m,a[i])-t;
pre[1]=1;
for(int i=2;i<=n;i++){
if(a[i]>a[i-1]) pre[i]=pre[i-1]+1;
else pre[i]=1;
ans=max(ans,pre[i]);
}
fix[n]=1;
for(int i=n-1;i>=1;i--){
if(a[i]<a[i+1]) fix[i]=fix[i+1]+1;
else fix[i]=1;
ans=max(ans,fix[i]);
} node T(1,m);
for(int i=n;i>=1;i--){
ans=max(ans,pre[i]+T.ask(a[i]+1,m));
if(arr[a[i]]<fix[i]){
arr[a[i]]=fix[i];
T.change(a[i],fix[i]);
}
} printf("%d\n",ans);
}
return 0;
}