Description
给定一个长度为 n 的有 k 个空缺的序列。
你有 m 个数可以用于填补空缺。
要求最大化最长上升子序列的长度。
n,m≤105,k≤103。
Solution
容易发现只需要先构造出 LIS 上的位置的值,对于其余未填位置随便填,所以构造 LIS 时就不需要考虑出现重复的问题。
考虑先求出最长上升子序列长度。
有一个显然的做法是维护一个数据结构,对于已经填了的位置就正常转移,对于没填的位置暴力枚举填的数然后转移,可以做到 O((n+m)k),但是常数会比较大。
有另一个求 LIS 的方法是维护 fi 表示当前长度为 i 的上升子序列的最小末尾值,gi 表示这个末尾的位置。每次加入一个 x,就二分求出 fj<x 的最大的 j,说明以 x 结尾的 LIS 长度为 j+1。又由于 fj+1≥x,就用 x 更新 fj+1 。对于没填的位置可以维护一个指针,从大往小枚举填的数同时更新指针即可。这样做常数就很小了。
然后考虑构造方案。
先在求 LIS 的过程中求出 leni 表示以 i 结尾的 LIS 长度,prei 表示以 i 结尾的 LIS 中 i 的前驱的位置。这时会发现对于 ai=−1 的位置这两个东西是维护不了的,构造方案时需要特殊处理。
为了方便构造方案,先在数组末尾加入 +∞。然后找到 LIS 的末尾位置 n+1,每次往前跳,可以在跳的过程中更新 −1 的位置的 len 值。设当前在 i,如果 ai 初始不为 −1,有两种情况:
- aprei 为 −1,就让 aprei 填 <ai 的最大可填值。
- aprei 不为 −1,就不用管。
然后就是 ai 初始为 −1(由于是从后往前构造的,所以 ai 此时已经填了数了),如果前面存在一个位置 j,使得 aj=−1,aj<ai,lenj=leni−1,那么 i 的前驱就为 j。否则一定是一个 −1 的位置,这里贪心选取最靠近 i 的那个 −1,同时填上 <ai 的最大可填值。填完往前跳即可。
时间复杂度:O((n+m)k)。
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>
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; while (T--) dickdreamer(); return 0; }
|