Codeforces 568E

怎么还有卡空间题。。。
设原来给定的序列为 a a a,能填的序列为 b b b
考虑经典LIS的 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn)做法,我们维护了一个数组 F [ i ] F[i] F[i]表示长度为 i i i的上升子序列的最小结尾,每次可以二分。我们可以魔改一下这个做法,对于 a i ≠ − 1 a_i \neq-1 ai=1的位置仍然二分,对于 a i = − 1 a_i=-1 ai=1的位置我们重新计算整个数组,如果预先对 b b b排序就可以two pointer了。这样是 O ( n log ⁡ n + ( n + m ) k ) \mathcal O(n\log n+(n+m)k) O(nlogn+(n+m)k)的。
不过我们现在要输出方案,一般的想法是对每个元素记录转移到它的位置,但是空间限制不容许我们对 a i = − 1 a_i=-1 ai=1的位置这么做。我们可以只记录 a i ≠ − 1 a_i\neq -1 ai=1的位置的前驱,倒序输出的时候我们只跳到 ≠ − 1 \neq -1 =1的位置,如果它的前驱仍然满足 ≠ − 1 \neq -1 =1就直接跳,否则我们尝试跳过前面 = − 1 =-1 =1的位置,即直接枚举前面第一个 ≠ − 1 \neq -1 =1的位置,预处理一些信息就可以 O ( 1 ) \mathcal O(1) O(1)判定了。
总时间复杂度仍然是 O ( n log ⁡ n + ( n + m ) k ) \mathcal O(n\log n+(n+m)k) O(nlogn+(n+m)k)

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define last last2
#define FR first
#define SE second

using namespace std;

typedef pair<int,int> pr;

int a[100005],b[100005];

pr f[100005],g[100005],q[100005];
int pre[100005];

void dp(int n,int m) {
  memset(f,0x3f,sizeof(f));
  f[0]=pr(0,0);
  int cnt=0,last=0;
  for(int i=1;i<=n+1;i++) {
  	pre[i]=last;
    if (a[i]!=-1) {
    	if (a[i]>f[cnt].FR) {
    		f[++cnt]=pr(a[i],i);
    		g[i]=pr(cnt,f[cnt-1].SE);
		}
    	else {
    		int t=lower_bound(f+1,f+cnt+1,pr(a[i],0))-f;
    		g[i]=pr(t,f[t-1].SE);
    		f[t]=pr(a[i],i);
		}
	}
    else {
    	int r=1,sz=0;
    	for(int j=1;j<=cnt+1;j++) {
    		while (r<=m&&b[r]<=f[j-1].FR) r++;
    		if (r>m) break;
    		if (b[r]<f[j].FR) q[++sz]=pr(j,b[r]);
		}
		for(int j=1;j<=sz;j++) f[q[j].FR]=pr(q[j].SE,i);
	    if (f[cnt+1].FR<inf) cnt++;
	    last=i;
	}
  }
}

int ans[100005],d[100005];
int c[100005];
bool vis[100005];

void output(int n,int m) {
  int sz=0;
  for(int i=1;i<=m;i++)
    if (i==1||b[i]!=b[i-1]) c[++sz]=b[i];
  int sz2=sz;
  for(int i=2;i<=m;i++)
    if (b[i]==b[i-1]) c[++sz2]=b[i];
  for(int i=0;i<=n;i++)
    if (a[i]!=-1) d[i]=upper_bound(c+1,c+sz+1,a[i])-c; 
  int x=n+1;
  while (x)
    if (a[g[x].SE]!=-1) x=g[x].SE;
    else {
		int s=0,t=lower_bound(c+1,c+sz+1,a[x])-c-1;
    	for(int i=x-1;i>=0;i--)
    	  if (a[i]!=-1) {
    	  	if (a[i]<a[x]&&g[i].FR+min(s,t-d[i]+1)+1==g[x].FR) {
    	  		int v=min(s,t-d[i]+1);
    	  		x=pre[x];
    	  		for(int j=1;j<=v;j++) {
    	  			ans[x]=c[t-j+1];
    	  			vis[t-j+1]=1;
    	  			x=pre[x];
				  }
    	  		x=i;
    	  		break;
			  }
		  } 
    	  else s++;
	}
  int r=1;
  for(int i=1;i<=n;i++)
    if (a[i]!=-1) ans[i]=a[i];
    else if (!ans[i]) {
    	while (r<=m&&vis[r]) r++;
    	vis[r]=1;
    	ans[i]=c[r];
	}
}

int main() {
  int n;
  scanf("%d",&n);
  for(int i=1;i<=n;i++) scanf("%d",&a[i]);
  a[n+1]=1e9+1;
  int m;
  scanf("%d",&m);
  for(int i=1;i<=m;i++) scanf("%d",&b[i]);
  sort(b+1,b+m+1);
  dp(n,m);
  output(n,m);
  for(int i=1;i<=n;i++) printf("%d ",ans[i]);
  printf("\n");
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值