zoukankan      html  css  js  c++  java
  • 【CF568E】Longest Increasing Subsequence(动态规划)

    点此看题面

    • 给定一个长度为(n)的序列,其中有(k)个空缺。
    • 你有(m)个数可以用于填补空缺(不能重复使用)。
    • 要求最大化最长上升子序列长度,给出一个方案。
    • (n,mle10^5,kle10^3)

    最长上升子序列的套路

    考虑最长上升子序列的常见(DP)套路,即设(f_i,g_i)分别表示使得最长上升子序列长度为(i)的最小值及其位置。

    然后对于已知的位置,它的值是固定的,我们再额外记(p_i,lst_i)表示以(i)为结尾的最长上升子序列长度以及转移的前一个位置。

    转移的时候,已知位置显然可以直接二分转移点转移。

    至于未知位置,个数(kle10^3),由于转移点随着填入值的单调变化也是单调移动的,我们可以直接双指针维护转移点转移。

    但这道题的难点应该在于方案,而方案的难点在于在最长上升子序列中的未知位置(不在最长上升子序列中的未知位置可以任意填入未填过的数)。

    对于一个未知位置,如果能在它前面找到一个长度比它恰少(1)且值比它小的已知位置,那么我们可以认为它是从这个位置转移过来的。

    否则,说明它必然是从一个未知位置转移过来的,那么不妨认为它就是从前一个未知位置转移的,显然不会使答案变劣。

    代码:(O(nlogn+mk))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define K 1000
    using namespace std;
    int n,m,a[N+5],b[N+5],c,s[N+5],p[N+5],lst[N+5];
    struct S
    {
    	int x,y;I S(CI a=0,CI b=0):x(a),y(b){}
    	I bool operator >= (Con S& o) Con {return x>=o.x;} 
    	I bool operator < (Con S& o) Con {return x<o.x;} 
    }f[N+5];
    int v[N+5];I void Fill(CI x,CI y)//求方案
    {
    	#define G(x) (lower_bound(b+1,b+m+1,x)-b-1)
    	if(y==1) return;RI k=lst[x];if(!k)
    	{
    		for(k=1;k^x;++k) if(~a[k]&&p[k]==y-1&&a[k]<a[x]) break;if(k==x) W(~a[--k]);//未知位置寻找其转移位置
    	}
    	return !~a[k]&&(a[k]=b[G(a[x])],v[G(a[x])]=1),Fill(k,y-1);//如果前一个位置未知,则给它一个尽可能大的值
    }
    int main()
    {
    	RI i,x,y,w,t=0;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",a+i);a[++n]=2e9;//在最后加一个极大值
    	for(scanf("%d",&m),i=1;i<=m;++i) scanf("%d",b+i);
    	for(sort(b+1,b+m+1),i=1;i<=m;++i) s[i]=b[i];c=unique(s+1,s+m+1)-s-1;//排序去重
    	for(i=1;i<=n;++i) if(~a[i])
    		f[p[i]=f[t]<a[i]?++t:lower_bound(f+1,f+t+1,a[i])-f]=S(a[i],i),lst[i]=f[p[i]-1].y;//已知位置直接二分转移
    	else for(f[t]<s[c]&&(f[++t]=S(s[c],i),0),x=c,y=t;x;f[y]=S(s[x--],i)) W(y^1&&f[y-1]>=s[x]) --y;//双指针维护转移点
    	for(Fill(f[t].y,t),i=x=1;i^n;printf("%d ",a[i++])) if(!~a[i]) {W(v[x]) ++x;a[i]=b[x++];}return 0;//输出方案
    }
    
  • 相关阅读:
    【洛谷3527】[POI2011] MET-Meteors(树状数组+整体二分)
    【洛谷1580】yyy loves Easter_Egg I(字符串处理题)
    【BZOJ4866】[YNOI2017] 由乃的商场之旅(莫队)
    【BZOJ4810】[YNOI2017] 由乃的玉米田(莫队+bitset)
    【洛谷1494】[国家集训队] 小Z的袜子(莫队)
    【BZOJ3668】[NOI2014] 起床困难综合症(位运算思想)
    【BZOJ3720】Gty的妹子树(主席树+时间分块)
    【BZOJ2427】[HAOI2010] 软件安装(缩点+树形DP)
    【洛谷3648】[APIO2014] 序列分割(斜率优化DP)
    动态规划专题(五)——斜率优化DP
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF568E.html
Copyright © 2011-2022 走看看