codeforces568E.Longest Increasing Subsequence

传送门:http://codeforces.com/problemset/problem/568/E

思路:首先没有空位,我们是记录一个low数组表示长度为i的上升子序列的最小结尾。

对于一个末尾新的数x,我们只要二分出一个位置low[i]<x<=low[i+1],即可更新low[i+1]的答案

现在有空位和m个数可用,每个数只能用一次。

其实每个数只能用一次这个条件并没有什么用,因为这是严格的LIS,用两次也没有用

所以可以先去重。

然后O(n+m)地枚举去更新low数组,因为空位数k<=1000,所以这是可以过的。

至于输出方案,记录

pre[i]i结尾的最长LIS的上一个位置(空位为-1)

f[i]i结尾LIS最长长度 

last[len] 长度为len的LIS最小结尾位置 (空位为-1)

倒序求方案

设当前位置为x

如果pre[x]!=-1,即上一位不是空位,直接x=pre[x]

否则枚举前面不是空位的一个位置y,填充好f[y]-f[x]个空位再令x=y即可

什么样的y可能在LIS中呢?

只要满足f[x]-f[y]=min(gap(x,y),num(x,y))即可

gap(l,r)为(l,r)间的空位数num(x,y)为m个数中有多少个可以填在a[x],a[y]之间

填完LIS后,无关紧要的空位随便填没有用过的数即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn=100010,inf=1e9;
using namespace std;
int n,m,a[maxn],b[maxn],low[maxn],pre[maxn],f[maxn],last[maxn],gap[maxn];
//low[len]长度为len的LIS最小结尾
//pre[i]i结尾的最长LIS的上一个位置,输方案要用
//f[i]i结尾LIS最长长度 
//last[len] 长度为len的LIS最小结尾位置 
void work(){
	memset(low,63,sizeof(low));
	pre[1]=0,f[1]=1,last[1]=1,low[1]=0;
	for (int k=2;k<=n;k++){
		if (a[k]!=-1){//有数 
			int l=1,r=n,mid=(l+r)>>1;
			while (l<=r){
				if (low[mid]<a[k]) l=mid+1;
				else r=mid-1;
				mid=(l+r)>>1;
			}
			pre[k]=last[l-1],last[l]=k;
			f[k]=l,low[l]=a[k];
		}
		else{//gap
			pre[k]=-1,f[k]=-1;
			for (int i=m,j=n;i>=1;i--){//从大到小枚举可填的数 
				while (low[j]>=b[i]) j--;//low有单调性,所以不用二分。 
				low[j+1]=b[i],last[j+1]=-1;
			}
		}
	}
//	for (int i=n;i;i--)if (low[i]>0&&low[i]!=1061109567) {printf("%d %d
",i,low[i]);break;}
}

void print(){//倒序求方案 
	int l,r,rest=m;//l,r表示b[l]-b[r]的数可以填在a[x]和a[y]之间 ,rest表示可用的数还有哪些 
	for (int x=n;x>=1;) if (a[x]!=-1){
		if (pre[x]>=0){x=pre[x];continue;}//LIS上一位有数,跳到上一位
		r=lower_bound(b+1,b+1+rest,a[x])-b-1;
		for (int y=x-1;y;y--)//否则枚举上一个不是空位的位置 
			if (a[y]!=-1&&a[y]<a[x]){
				l=upper_bound(b+1,b+1+rest,a[y])-b;
				if (f[x]-f[y]-1==min(r-l+1,gap[x]-gap[y])){//如果满足,位置y就在一种LIS中 
					for (int i=y,j=l;i<=x&&j<=r;i++)
						if (a[i]==-1) a[i]=b[j],b[j]=-1,j++;//填充y到x这段区间 
					x=y,rest=l-1;break;//继续向前做 
				}
			}
	}
	for (int i=1,j=1;i<=n;i++){
		if (a[i]==-1){
			for (;b[j]==-1;j++);
			a[i]=b[j],b[j++]=-1;
		}
	}
	for (int i=2;i<n;i++) printf("%d ",a[i]);puts("");
}

int main(){
	scanf("%d",&n),gap[1]=gap[0]=0;
	for (int i=2;i<=n+1;i++) scanf("%d",&a[i]),gap[i]=gap[i-1]+(a[i]==-1);
	a[1]=0,a[n+2]=inf,gap[n+2]=gap[n+1],n+=2;
	scanf("%d",&m);for (int i=1;i<=m;i++) scanf("%d",&b[i]);
	sort(b+1,b+1+m),m=unique(b+1,b+1+m)-b-1;//LIS是严格上升的,所以每个数最多用一次 
	work(),print();
	return 0;
}
/*
100
-1 2 3 74 90 39 37 18 23 -1 5 -1 56 88 99 49 72 11 19 6 81 24 8 23 64 -1 100 77 61 87 23 -1 20 15 -1 55 25 40 4 25 73 85 87 72 5 98 46 49 -1 67 81 58 3 -1 22 14 -1 -1 92 -1 78 53 64 23 84 10 -1 54 83 55 24 -1 79 23 92 41 -1 -1 93 -1 59 90 64 93 95 22 -1 67 -1 33 41 84 37 73 -1 -1 18 49 50 58
20
57 23 22 93 77 19 85 32 79 94 20 59 78 35 16 92 33 94 42 11
*/


原文地址:https://www.cnblogs.com/thythy/p/5493515.html