Link
求LIS有一个贪心+二分的做法:维护(F_i)表示长度为(i)的LIS的最小末尾。
对于(a_i=-1)的位置,我们可以先把(b)排序之后利用双指针重新计算(F),时间复杂度为(O(nlog n+(n+m)k))。
现在还需要输出方案,一个普遍的做法是记录每个元素的前驱,但是对于(a_i=-1)的部分开不下这么大的空间。
实际上可以只记录(a_i
e-1)的前驱,(a_i=-1)的位置只需要在求LIS的时候预处理相关信息即可。
从后往前还原答案时,对于LIS中的(a_i=-1)的位置,贪心地选择最大的合法的数即可。
#include<cctype>
#include<cstdio>
#include<utility>
#include<algorithm>
using pi=std::pair<int,int>;
const int N=100007;
int a[N],b[N],pre1[N],pre2[N],vis[N];pi f[N];
int read(){int x=0,c=getchar(),f=1;while(isspace(c))c=getchar();if(c=='-')f=-1,c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return f*x;}
int find(int x){return !~a[f[x].second]? pre2[x]:f[x].second;}
int main()
{
int n,m,len=0,las=2e9;
n=read();
for(int i=1;i<=n;++i) a[i]=read();
m=read();
for(int i=1;i<=m;++i) b[i]=read();
std::sort(b+1,b+m+1);
for(int i=1;i<=n;++i)
if(~a[i])
{
int l=1,r=len+1,mid;
while(l<r) f[mid=(l+r)/2].first<a[i]? l=mid+1:r=mid;
f[l]={a[i],i},pre2[l]=pre1[i]=find(l-1),len+=len<l;
}
else
{
len+=b[m]>f[len].first;
for(int j=m,k=len;j;f[k]={b[j--],i},pre2[k]=find(k-1)) for(;k>1&&f[k-1].first>=b[j];--k);
}
for(int i=find(len),j=n,k=m;;las=a[i],i=pre1[i])
{
for(;j>i;--j) if(!~a[j]) {for(;b[k]>=las;--k);if(k&&b[k]>a[i])las=a[j]=b[k],vis[k--]=1;}
if(!i) break;
}
for(int i=1,k=1;i<=n;++i) if(!~a[i]) {for(;k<=m&&vis[k];++k);a[i]=b[k++];}
for(int i=1;i<=n;++i) printf("%d ",a[i]);
}