[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;
}
posted @ 2021-02-01 21:27  evenbao  阅读(61)  评论(0编辑  收藏  举报