题目链接:E. Longest Increasing Subsequence
题目大意:洛谷
题解:首先如果要求的是最长上升子序列的长度是很简单的。
因为是严格单调递增,所以每一个数在最长上升子序列中最多只会出现一次,所以对于(b)数组中的每一个数只能够取一次的限制可以不考虑,直接考虑传统的(O(nlog n))做法,那么对于正常的位置,正常地二分做,对于(-1)的位置,可以枚举当前位置选哪一个做。
这一部分的时间复杂度是(O(nlog n+mk))的。
接下来考虑还原方案。
先考虑没有(-1)的情况。
因为不是(O(n^2))的 DP,所以我们在转移(其实就是二分查找中的那一步,姑且称之为转移吧)的时候需要记录它的上一个位置,这样的话我们就可以倒推出来原序列的长度了。
因为如果需要找最大值的位置的话比较麻烦,所以我们可以直接在序列末尾添加上一个( ext{Inf})。
接下来考虑(-1)应当怎么处理。
直接在(b)数组中找到小于当前数的最大的数填进去就可以了。
总时间复杂度(O(nlog n +mlog 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;
}