zoukankan      html  css  js  c++  java
  • 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
    */


  • 相关阅读:
    springboot自动装配mybatisplus时,凭啥MybatisPlusAutoConfiguration比MybatisAutoConfiguration先装配
    mybatis 整合 spring 时,mapper 是怎么被设置必要的参数的
    canvas 画的线无法清除的问题
    大学英语单词 第二单元
    快乐纪中(二)2
    jzoj 2644. 数列
    jzoj【NOIP2011模拟10.31】T1游戏
    快乐纪中
    树形DP
    炮兵阵地
  • 原文地址:https://www.cnblogs.com/thythy/p/5493515.html
Copyright © 2011-2022 走看看