Codeforces 568E. Longest Increasing Subsequence 题解

题目链接:E. Longest Increasing Subsequence

题目大意:洛谷


题解:首先如果要求的是最长上升子序列的长度是很简单的。

因为是严格单调递增,所以每一个数在最长上升子序列中最多只会出现一次,所以对于\(b\)数组中的每一个数只能够取一次的限制可以不考虑,直接考虑传统的\(O(n\log n)\)做法,那么对于正常的位置,正常地二分做,对于\(-1\)的位置,可以枚举当前位置选哪一个做。

这一部分的时间复杂度是\(O(n\log n+mk)\)的。

接下来考虑还原方案。

先考虑没有\(-1\)的情况。

因为不是\(O(n^2)\)的 DP,所以我们在转移(其实就是二分查找中的那一步,姑且称之为转移吧)的时候需要记录它的上一个位置,这样的话我们就可以倒推出来原序列的长度了。

因为如果需要找最大值的位置的话比较麻烦,所以我们可以直接在序列末尾添加上一个\(\text{Inf}\)

接下来考虑\(-1\)应当怎么处理。

直接在\(b\)数组中找到小于当前数的最大的数填进去就可以了。

总时间复杂度\(O(n\log n +m\log m +(n+m)k)\)

代码:

#include <cstdio>
#include <algorithm>
using namespace std;
void read(int &a){
	a=0;
	int f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-'){
			f=-1;
		}
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		a=(a<<1)+(a<<3)+(c^48);
		c=getchar();
	}
	if(f==-1){
		a=-a;
	}
}
const int Maxn=100000;
const int Maxk=1000;
const int Inf=0x7f7f7f7f;
int n,m;
int a[Maxn+5],b[Maxn+5];
bool vis[Maxn+5];
int f[Maxn+5];
int g[Maxn+5];
int p[Maxn+5],pos[Maxn+5];
int ans[Maxn+5];
void get(int i,int k,int &x){
	int tmp=lower_bound(b+1,b+1+m,k)-b-1;
	vis[tmp]=1;
	x=ans[i]=b[tmp];
}
int main(){
	read(n);
	for(int i=1;i<=n;i++){
		read(a[i]);
		f[i]=Inf;
	}
	n++;
	a[n]=Inf;
	f[n]=Inf;
	read(m);
	for(int i=1;i<=m;i++){
		read(b[i]);
	}
	sort(b+1,b+1+m);
	for(int i=1;i<=n;i++){
		if(a[i]==-1){
			for(int j=n,k=m;k>0;k--){
				while(f[j]>=b[k]){
					j--;
				}
				f[j+1]=b[k];
				pos[j+1]=i;
			}
		}
		else{
			int j=lower_bound(f+1,f+1+n,a[i])-f-1;
			g[i]=j+1;
			p[i]=pos[j];
			f[j+1]=a[i];
			pos[j+1]=i;
		}
	}
	{
		int i=g[n],j=n,x=a[n];
		while(i--){
			if(a[j]!=-1){
				if(a[p[j]]==-1){
					get(p[j],a[j],x);
				}
				else{
					x=a[p[j]];
				}
				j=p[j];
			}
			else{
				bool flag=0;
				for(int s=j-1;s>0;s--){
					if(a[s]!=-1&&g[s]==i&&a[s]<x){
						x=a[j=s];
						flag=1;
						break;
					}
				}
				if(flag){
					continue;
				}
				for(int s=j-1;s>0;s--){
					if(a[s]==-1){
						get(s,x,x);
						j=s;
						break;
					}
				}
			}
		}
	}
	for(int i=1,j=1;i<n;i++){
		if(a[i]==-1){
			if(ans[i]!=0){
				a[i]=ans[i];
				continue;
			}
			while(vis[j]){
				j++;
			}
			vis[j]=1;
			a[i]=b[j];
		}
	}
	for(int i=1;i<n;i++){
		printf("%d ",a[i]);
	}
	puts("");
	return 0;
}
posted @ 2020-08-26 21:35  with_hope  阅读(165)  评论(0)    收藏  举报