CF568E Longest Increasing Subsequence 题解

Description

给定一个长度为 nn 的有 kk 个空缺的序列。

你有 mm 个数可以用于填补空缺。

要求最大化最长上升子序列的长度。

n,m105n, m \le 10^5k103k \le 10^3

Solution

容易发现只需要先构造出 LIS 上的位置的值,对于其余未填位置随便填,所以构造 LIS 时就不需要考虑出现重复的问题。

考虑先求出最长上升子序列长度。

有一个显然的做法是维护一个数据结构,对于已经填了的位置就正常转移,对于没填的位置暴力枚举填的数然后转移,可以做到 O((n+m)k)O\left((n+m)k\right),但是常数会比较大。

有另一个求 LIS 的方法是维护 fif_i 表示当前长度为 ii 的上升子序列的最小末尾值,gig_i 表示这个末尾的位置。每次加入一个 xx,就二分求出 fj<xf_j<x 的最大的 jj,说明以 xx 结尾的 LIS 长度为 j+1j+1。又由于 fj+1xf_{j+1}\geq x,就用 xx 更新 fj+1f_{j+1} 。对于没填的位置可以维护一个指针,从大往小枚举填的数同时更新指针即可。这样做常数就很小了。

然后考虑构造方案。

先在求 LIS 的过程中求出 lenilen_i 表示以 ii 结尾的 LIS 长度,preipre_i 表示以 ii 结尾的 LIS 中 ii 的前驱的位置。这时会发现对于 ai=1a_i=-1 的位置这两个东西是维护不了的,构造方案时需要特殊处理。

为了方便构造方案,先在数组末尾加入 ++\infty。然后找到 LIS 的末尾位置 n+1n+1,每次往前跳,可以在跳的过程中更新 1-1 的位置的 lenlen 值。设当前在 ii,如果 aia_i 初始不为 1-1,有两种情况:

  1. apreia_{pre_i}1-1,就让 apreia_{pre_i}<ai<a_i 的最大可填值。
  2. apreia_{pre_i} 不为 1-1,就不用管。

然后就是 aia_i 初始为 1-1(由于是从后往前构造的,所以 aia_i 此时已经填了数了),如果前面存在一个位置 jj,使得 aj1,aj<ai,lenj=leni1a_j\neq -1,a_j<a_i,len_j=len_i-1,那么 ii 的前驱就为 jj。否则一定是一个 1-1 的位置,这里贪心选取最靠近 ii 的那个 1-1,同时填上 <ai<a_i 的最大可填值。填完往前跳即可。

时间复杂度:O((n+m)k)O\left((n+m)k\right)

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e5 + 5;

int n, m;
int a[kMaxN], b[kMaxN], pre[kMaxN], len[kMaxN], f[kMaxN], g[kMaxN];
bool vis[kMaxN];

void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
std::cin >> m;
for (int i = 1; i <= m; ++i) std::cin >> b[i];
std::sort(b + 1, b + 1 + m);
a[n + 1] = 1e9 + 1;
memset(f, 0x3f, sizeof(f)), memset(pre, -1, sizeof(pre));
f[0] = g[0] = 0;
for (int i = 1; i <= n + 1; ++i) {
if (~a[i]) {
int j = std::lower_bound(f, f + 2 + n, a[i]) - f - 1;
len[i] = j + 1, pre[i] = g[j], f[j + 1] = a[i], g[j + 1] = i;
} else {
for (int j = m, k = n + 1; j; --j) {
for (; ~k && f[k] >= b[j]; --k) {}
f[k + 1] = b[j], g[k + 1] = i;
}
}
}
for (int i = n + 1; len[i] > 1;) {
if (!~pre[i]) {
for (int j = i - 1; j; --j) {
if (~a[j] && len[j] == len[i] - 1 && a[j] < a[i]) {
pre[i] = j; break;
}
}
if (!~pre[i]) {
for (int j = i - 1; j; --j) {
if (!~a[j]) {
pre[i] = j; break;
}
}
}
}
if (!~a[pre[i]]) {
int j = std::lower_bound(b + 1, b + 1 + m, a[i]) - b - 1;
a[pre[i]] = b[j], vis[j] = 1, len[pre[i]] = len[i] - 1;
}
i = pre[i];
}
for (int i = 1, j = 1; i <= n; ++i) {
if (!~a[i]) {
for (; vis[j]; ++j) {}
a[i] = b[j], vis[j] = 1;
}
}
for (int i = 1; i <= n; ++i) std::cout << a[i] << ' ';
}

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;
}

CF568E Longest Increasing Subsequence 题解
https://sobaliuziao.github.io/2024/08/06/post/295c6906.html
作者
Egg_laying_master
发布于
2024年8月6日
许可协议