CF568E Longest Increasing Subsequence

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]);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12857439.html