[CF568E] Longest Increasing Subsequence
[题目链接]
https://codeforces.com/contest/568/problem/E
[题解]
首先注意到一个数最多出现在一个上升子序列中 , 故可以忽略重复数字的影响。
不妨令 \(l_{i} , p_{i}\) 分别表示当 \(i\) 非 "空" 时 , 以 \(i\) 为结尾的最长上升子序列长度和上一项的位置。
接着 , 令 \(f_{i} , g_{i}\) 表示长度为 \(i\) 的最长上升子序列长度和最后一项位置。
从前到后以此加数 , 假设当前加入的是第 \(i\) 个数。
若 \(i\) 非 "空" , 考虑经典的 \(O(NlogN)\) 求解 \(LIS\) 做法。 设当前位置上的数为 \(x\) , 那么在 \(f\) 上找到 \(< x\) 的最大 \(f_{j}\) , 并令 \(l_{i} = j + 1\) , \(p_{i} = g_{j}\) , \(g_{j + 1} = i\)。
若该位置空缺 , 则从大到小枚举可插入的数并更新 \(f , g\) 数组。这样避免了二分的问题 , 只需用一个指针扫描即可。
这样就可以计算出最长上升子序列的长度了。
接着考虑还原序列 , 不妨倒着考虑 , 设此时已经还原到了第 \(i\) 个数 \(x\) , 其在原序列中位置为 \(j\)。
若该位置非空 , 则可以直接用 \(p_{j}\) 找到上一个数。
否则 , 现在 \(j\) 前找到第一个数 \(s\) 使得 \(l_{s} = i - 1\) 且 \(s\) 上的数比 \(x\) 小 , 如果找不到 , 上一个位置就是上一个空位 , 其权值为 \(< x\) 的最大值。
时间复杂度 : \(O(NlogN + K(N + M))\)
[代码]
# include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MN = 2e5 + 5 , INF = 2e9;
int N , M , vis[MN] , A[MN] , F[MN] , L[MN] , G[MN] , P[MN] , ans[MN] , B[MN];
inline void get(int i , int k , int &x) {
int o = lower_bound(B + 1 , B + 1 + M , k) - B - 1;
vis[o] = 1; x = ans[i] = B[o];
}
int main() {
scanf("%d" , &N);
for (int i = 1; i <= N; ++i) scanf("%d" , &A[i]) , F[i] = INF;
++N , A[N] = F[N] = INF;
scanf("%d" , &M);
for (int i = 1; i <= M; ++i) scanf("%d" , &B[i]);
sort(B + 1 , B + 1 + M);
for (int i = 1; i <= N; ++i)
if (~A[i]) {
int j = lower_bound(F + 1 , F + 1 + N , A[i]) - F - 1;
L[i] = j + 1 , P[i] = G[j] , F[j + 1] = A[i] , G[j + 1] = i;
} else {
for (int j = N , o = M; o; --o) {
while (F[j] >= B[o]) --j;
F[j + 1] = B[o] , G[j + 1] = i;
}
}
int i = L[N] , j = N , x = A[N];
while (i--) {
if (~A[j]) {
if (!~A[P[j]]) get(P[j] , A[j] , x);
else x = A[P[j]];
j = P[j];
} else {
bool ok = 0;
for (int s = j - 1; s; --s)
if (~A[s] && L[s] == i && A[s] < x) {
x = A[j = s] , ok = 1;
break;
}
if (ok) continue;
for (int s = j - 1; s; --s)
if (!~A[s]) {
get(s , x , x); j = s;
break;
}
}
}
for (int i = 1 , j = 1; i <= N; ++i) {
if (!~A[i]) {
if (ans[i]) continue;
while (vis[j]) ++j;
vis[j] = 1 , ans[i] = B[j];
} else ans[i] = A[i];
}
for (int i = 1; i < N; ++i) printf("%d " , ans[i]);
printf("\n");
return 0;
}