CF2124H Longest Good Subsequence 题解

Description

将一个长度为 mm 的数组 bb 称为 好数组(good),如果它满足以下两个条件:

  1. 对于每个 1im1 \leq i \leq m,都有 1bii1 \leq b_i \leq i

  2. 存在一个长度为 mm 的排列 pp,使得对于每个 1im1 \leq i \leq m,都有:
    bib_i 是使得区间 [bi,i][b_i, i] 中的最小值恰好等于 pip_i最小下标,也就是满足

    min(pbi,pbi+1,,pi)=pi\min(p_{b_i}, p_{b_i+1}, \dots, p_i) = p_i

例如,数组 [1,1,3,3,5][1,1,3,3,5] 是一个好数组(可以取排列 p=[2,1,5,3,4]p = [2,1,5,3,4] 来满足第二个条件);
而数组 [1,1,2][1,1,2] 不是好数组。

注意:空数组被视为好数组。


现在给定一个长度为 nn 的数组 aa,你需要找出其中最长的好子序列的长度。

n15000n\leq 15000

Solution

考虑什么样的数组 bb 是合法的。

显然需要满足 bbi1b_{b_i-1}bib_i 左边第一个比 bib_i 小的数,那么由于 bbibi>bbi1b_{b_i}\geq b_i>b_{b_i-1},所以 bbi=bib_{b_i}=b_i

考虑对 bx=xb_x=x 的这些位置进行区间 dp。

具体地,设 fi,jf_{i,j} 表示只考虑 [i,j][i,j] 中大于等于 aia_i 的数,ii 必须选,且 ii 是第 aia_i 个选的数的最长长度。注意这里由于已经钦定 ii 的位置了,所以就不管前面是否能选以及 aia_i 这里是否满足条件,把这个区间内的数看成一个独立的个体。

有如下几种转移:

  1. aj<aia_j<a_i,则 fi,jfi,j1f_{i,j}\leftarrow f_{i,j-1}

  2. aj=aia_j=a_i,则 fi,jfi,j1+1f_{i,j}\leftarrow f_{i,j-1}+1

  3. aj>aia_j>a_i,现在需要为 aja_j 找到其对应的开头 kk,满足 ak=aja_k=a_j,且 kk 可以作为第 aka_k 个数出现。

    这个显然等价于 fi,k1ak1f_{i,k-1}\geq a_k-1,因为由于这个子序列支持从末尾删除,所以可以得到一个长度为 ak1a_k-1 的子序列,而 biib_i\leq i,所以前面的数都小于等于 ak1a_k-1,此时 aka_k 就可以作为开头了。

    转移为 fi,jmax{fi,j1,fk,j}f_{i,j}\leftarrow\max\{f_{i,j-1},f_{k,j}\},由于只需要找到最小的满足条件的 kk,扫右端点的时候维护一个数组即可。

答案即为所有满足 ai=1a_i=1iifi,nf_{i,n} 的最大值。

时间复杂度:O(n2)O(n^2)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1.5e4 + 5;

int n;
int a[kMaxN], f[kMaxN][kMaxN];

void chkmax(int &x, int y) { x = (x > y ? x : y); }
void chkmin(int &x, int y) { x = (x < y ? x : y); }

void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
int ans = 0;
for (int i = n; i; --i) {
static int fir[kMaxN];
std::fill_n(fir + 1, n, 0);
f[i][i] = a[i];
for (int j = i + 1; j <= n; ++j) {
f[i][j] = f[i][j - 1];
if (a[j] < a[i]) continue;
if (a[j] == a[i]) chkmax(f[i][j], f[i][j - 1] + 1);
if (!fir[a[j]] && f[i][j - 1] + 1 >= a[j]) fir[a[j]] = j;
if (fir[a[j]]) chkmax(f[i][j], f[fir[a[j]]][j]);
}
if (a[i] == 1) chkmax(ans, f[i][n]);
}
std::cout << ans << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

CF2124H Longest Good Subsequence 题解
https://sobaliuziao.github.io/2025/07/20/post/672c783f.html
作者
Egg_laying_master
发布于
2025年7月20日
许可协议