题面$
link
定义了长度为
k
k
k 的几乎递增序列
{
b
i
}
\{b_i\}
{bi} 为:
m
i
n
(
b
1
,
b
2
)
≤
m
i
n
(
b
2
,
b
3
)
≤
.
.
.
m
i
n
(
b
k
−
1
,
b
k
)
min(b_1, b_2) ≤ min(b_2, b_3) ≤ ... min(b_{k - 1}, b_k)
min(b1,b2)≤min(b2,b3)≤...min(bk−1,bk)。
现在给定了一个长度为
n
(
n
≤
5
e
5
)
n \ (n ≤ 5e5)
n (n≤5e5) 的数组
{
a
i
}
(
1
≤
a
i
≤
n
)
\{a_i\} \ ( 1 ≤a_i ≤ n)
{ai} (1≤ai≤n),求几乎递增子序列的最长长度。
分析
我们可以得到,对于一个长度为
k
k
k 的几乎递增序列
{
b
i
}
(
1
≤
i
≤
k
)
\{b_i\} (1 ≤ i ≤ k)
{bi}(1≤i≤k) ,当
i
≥
3
i ≥ 3
i≥3 时,有
b
i
≥
m
i
n
(
b
i
−
1
,
b
i
−
2
)
b_i ≥ min(b_{i-1}, b_{i-2})
bi≥min(bi−1,bi−2) 成立,即要么大于等于它前一个数,要么大于等于其前前一个数。如果我们定义了
b
0
=
0
b_0 = 0
b0=0,那么对于
i
≥
2
i ≥ 2
i≥2 都成立。
如果定义以
b
i
b_i
bi 结尾的几乎递增序列的最大长度为
d
p
[
i
]
dp[i]
dp[i] ,则有三种转移情况:
①为一个子序列的首部,即
d
p
[
i
]
=
1
dp[i] = 1
dp[i]=1;
②对于已有子序列
{
b
m
1
,
b
m
2
,
.
.
.
b
m
k
}
\{b_{m1}, b_{m2}, ...b_{mk}\}
{bm1,bm2,...bmk},有
b
i
≥
b
m
k
b_i ≥ b_{m_k}
bi≥bmk,即大于等于最后一个数,也就是对于
j
<
i
j < i
j<i, 满足
a
[
j
]
≤
a
[
i
]
a[j] ≤ a[i]
a[j]≤a[i],则
d
p
[
i
]
=
m
a
x
(
d
p
[
j
]
+
1
)
dp[i] = max(dp[j] + 1)
dp[i]=max(dp[j]+1);
③对于已有子序列
{
b
m
1
,
b
m
2
,
.
.
.
b
m
k
−
1
,
b
m
k
}
\{b_{m1}, b_{m2}, ...b_{mk-1}, b_{mk}\}
{bm1,bm2,...bmk−1,bmk},若
b
m
k
−
1
≥
b
m
k
b_{mk-1} ≥ b_{mk}
bmk−1≥bmk,那么肯定也能从
b
m
k
b_{mk}
bmk 转移过来,我们这里就不考虑,只考虑
b
m
k
−
1
<
b
m
k
b_{mk-1} < b_{mk}
bmk−1<bmk 的情况,也就是对于
j
1
<
j
2
<
i
j_1 < j_2 < i
j1<j2<i, 满足
a
[
j
1
]
<
a
[
j
2
]
,
a
[
j
1
]
≤
a
[
i
]
a[j_1] < a[j_2], \ a[j_1] ≤ a[i]
a[j1]<a[j2], a[j1]≤a[i],则
d
p
[
i
]
=
m
a
x
(
d
p
[
j
1
]
+
2
)
dp[i] = max(dp[j_1] + 2)
dp[i]=max(dp[j1]+2);
对于 ①、②两种情况,由于
{
a
i
}
(
1
≤
a
i
≤
n
)
\{a_i\} \ ( 1 ≤a_i ≤ n)
{ai} (1≤ai≤n),可以分别用两棵线段树来记录当前满足条件的值的最大值,如①,可以在第一棵线段树查询最后一个值在
[
0
,
a
[
i
]
]
[0, a[i]]
[0,a[i]] 的最长几乎递增序列,然后去更新
a
[
i
]
a[i]
a[i],对于②的话,由于需要右侧有一个数大于它,所以需要用单调栈来维护一下,当它右侧第一个大于它的数被更新后,才能更新它。
#include <bits/stdc++.h>
#include <iostream>
#include <set>
#define here printf("modassing [%s] in LINE %d\n", __FUNCTION__, __LINE__);
#define debug(x) cout << #x << ":\t" << (x) << endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int maxn = 5e5 + 10;
const int maxm = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const ll mod = 998244353;
const double pi = acos(-1.0);
class Segment_Tree
{
int tree[maxn << 2];
public:
void build(int node, int l, int r)
{
if(l == r)
{
tree[node] = -INF;
return;
}
int mid = (l + r) / 2;
build(node * 2, l, mid);
build(node * 2 + 1, mid + 1, r);
tree[node] = -INF;
}
int query(int node, int l, int r, int x, int y)
{
if(x <= l && y >= r)
return tree[node];
int ans = -INF, mid = (l + r) / 2;
if(x <= mid) ans = max(query(node * 2, l, mid, x, y), ans);
if(y > mid) ans = max(query(node * 2 + 1, mid + 1, r, x, y), ans);
return ans;
}
void update(int node, int l, int r, int x, int c)
{
if(l == r)
{
tree[node] = max(tree[node], c);
return;
}
int mid = (l + r) / 2;
if(x <= mid) update(2*node, l, mid, x, c);
else update(2 * node + 1, mid + 1, r, x, c);
tree[node] = max(tree[node * 2], tree[node * 2 + 1]);
}
}t1, t2;
struct node
{
int id, nxt;
bool operator<(const node& m)
{
return nxt < m.nxt;
}
} pro[maxn];
int t, n, a[maxn], dp[maxn];
int main()
{
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) pro[i].id = i;
stack<P> st;
for (int i = 1; i <= n; i++)
{
while(!st.empty() && a[i] >= st.top().first)
{
pro[st.top().second].nxt = i;
st.pop();
}
st.push(P(a[i], i));
}
while(!st.empty())
{
pro[st.top().second].nxt = n + 1;
st.pop();
}
//for (int i = 1; i <= n; i++)
//cout << pro[i].nxt << endl;
sort(pro + 1, pro + 1 + n);
int cur = 0;
t1.build(1, 0, n);
t2.build(1, 0, n);
t1.update(1, 0, n, 0, 0);
for (int i = 1; i <= n; i++)
{
dp[i] = max({1, t1.query(1, 0, n, 0, a[i]) + 1, t2.query(1, 0, n, 0, a[i]) + 2});
//cout << i << " " << dp[i] << endl;
while(cur < n && pro[cur].nxt <= i)
{
t2.update(1, 0, n, a[pro[cur].id], dp[pro[cur].id]);
cur++;
}
t1.update(1, 0, n, a[i], dp[i]);
}
int ans = 1;
for (int i = 1; i <= n; i++) ans = max(ans, dp[i]);
printf("%d\n", ans);
}
}