首页 > 其他分享 >CF568E Longest Increasing Subsequence 题解

CF568E Longest Increasing Subsequence 题解

时间:2023-10-19 09:44:20浏览次数:44  
标签:空位 int 题解 pos Subsequence LIS Increasing 倒推 las

Longest Increasing Subsequence

LIS 问题有两种主流 \(O(n\log n)\) 解法,最常见的树状数组法,以及不那么常见的二分法。然后考虑本题,发现一个神奇的思路就是求出 LIS 后倒序复原出数组。

进一步思考后发现,因为本题是 LIS(Longest Increasing Subsequence) 而非 LNDS(Longest Non-Decresing Subsequence),所以任意值在 LIS 中只出现一次,所以我们完全可以不用纠结“一个数只能补一个空缺的位置”这个限制,直接忽略它,求出 LIS 时所有在 LIS 中的空缺的填补方法,然后再用尚未被填补的数来补尚未被填补的位置即可。

然后就轮到我们考虑使用哪种 LIS 了。树状数组法似乎不好扩展(除非把每个空位填哪个数都给枚举一遍扔进 BIT 里面,但是这样很明显一共要扔 \(10^3\times10^5=10^8\) 次,单次 \(O(1)\) 还勉强可以,\(O(\log n)\) 你在想桃子),因此我们不妨考虑一下二分法。

于是我们发现二分法是可以的,因为其状态 \(f_i\) 表示长度为 \(i\) 的 LIS 的末尾数最小可能是多少,在非空位处直接二分,在空位处原本也要枚举填哪个数然后二分,但是很明显可以用 two-pointers 轻松 \(O(n)\) 处理。于是复杂度便变为 \(n\log n+k(n+m)\)。

于是我们现在便可以求出 LIS 长度,考虑复原序列。

我们发现肯定要求出一个 \(g_i\) 表示以位置 \(i\) 结尾的 LIS 的最长长度。为了实现快速倒推,还要对非空位处的 \(g\) 求出其前驱位置,记作 \(las_i\)。而 \(las\) 又可以通过在 DP 的过程中对每个 \(f\) 维护该最小的末尾数的位置 \(pos\) 来求出。

于是我们考虑倒推。对于一个非空缺的位置,我们显然可以通过 \(las\) 直接倒推;对于一个空缺的位置,我们考虑优先倒推到非空的位置(这个可以通过枚举得到);否则,即其无法倒推到非空位置,也即其必倒推到空位,而此时我们肯定想贪心地倒推到最后一个空位,那就倒吧。

时间复杂度如上,即为 \(n\log n+k(n+m)\)。

代码:

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=1e5+100;
int n,m,lim;
int a[N],b[N],f[N],pos[N],g[N],las[N];
multiset<int>s;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    cin>>m;
    for(int i=1;i<=m;i++) cin>>b[i],s.insert(b[i]);
    sort(b+1,b+m+1);
    memset(f,INF,sizeof(f));
	f[0]=0;
    for(int i=1;i<=n;i++){
        if(a[i]!=-1){
            int p=lower_bound(f+1,f+n+1,a[i])-f;
            if(f[p]>a[i]){
				f[p]=a[i];
				pos[p]=i;
			}
            g[i]=p;
			las[i]=pos[p-1];
        }else{
            for(int j=m,k=i;j;j--){
                while(k&&f[k-1]>=b[j])k--;
                if(k&&f[k]>b[j]){
					f[k]=b[j];
					pos[k]=i;
				}
                g[i]=max(g[i],k);
            }
        }
    }
    int i=n;
    while(f[i]==INF) i--;
    for(int j=pos[i],nex=INF;i;i--){
        if(a[j]!=-1){
			nex=a[j];
			j=las[j];
		}else{
            s.erase(s.find(nex=a[j]=*(lower_bound(b+1,b+m+1,nex)-1)));
            bool fd=false;
            for(int k=j-1;k;k--){
				if(a[k]!=-1&&g[k]==i-1&&a[k]<a[j]){
					j=k;
					fd=true;
					break;
				}
			}
            if(fd) continue;
            while(--j) if(a[j]==-1) break;
        }
    }
    for(int i=1;i<=n;i++){
		if(a[i]==-1){
			a[i]=*s.begin();
			s.erase(s.begin());
		}
	}
    for(int i=1;i<=n;i++) cout<<a[i]<<' ';
    return 0;
}

标签:空位,int,题解,pos,Subsequence,LIS,Increasing,倒推,las
From: https://www.cnblogs.com/xuantianhao/p/17773987.html

相关文章

  • [AGC046D] Secret Passage 题解
    SecretPassage稍微观察一下就能发现,任一时刻,我们剩下的东西必然是一段定死了的后缀,加上一些可以任意塞位置的0与1。考虑任意一个由上述时刻生成的串,就会发现它与该后缀的最长公共子序列长度即为后缀长度,且还剩余一些0与1。于是考虑模拟最长公共子序列的过程。设\(g_{i,j......
  • Atcoder Beginner Contest 324 G Generate Arrays 题解-Treap
    为了更好的阅读体验,请点击这里题目链接套上平衡树板子就能做的很快的题,然后因为是指针存树,因此交换只需要把序列大小较小的挨个拿出来插到相应的地方即可。复杂度\(O(N\log^2N)\)。但是一定要记住不可以直接使用std::swap交换包含带有指针的类的实例(如代码中的Treap类)!......
  • 精选题解汇总
    Part1比赛题解CF1873CF1203CF1234CF1249Part2难题题解P1124P6346P2198P7974P4814......
  • P1124 题解
    题目大意一个长度为\(n\)的字符串\(S\),进行以下操作。假设\(s\)为acbdef,每一次将首字母移至末尾,得到\(6\)个字符串:acbdefcbdefabdefacdefacbefacbdfacbde将每个字符串的首字母排序:acbdefbdefaccbdefadefacbefacbdfacbde每个字符串的末尾连在一起为fcab......
  • Linux 环境下(Ubuntu)webbench的安装问题解决与使用
    webbench最多可以模拟3万个并发连接去测试网站的负载能力。并发能力比较高,可以测试https及动态静态页面。适合中小型网站测试承受能力。原理:父进程fork若干个子进程,每个子进程在用户要求时间或默认的时间内对目标web循环发出实际访问请求,父子进程通过管道进行通信,子进程通过......
  • P6346 题解
    题目大意如果\(\texttt{Kevin}\)想和第\(i\)个人交朋友,要么需要认识\(a_i\)个人,要么付出\(b_i\)的代价,他让你使\(\texttt{Kevin}\)与所有的人交朋友。解题思路如果想水到\(15\)分,也就是所有\(b_i\)都等于\(1\)的情况,那我们可以直接排个序,然后遍历一下每一个人,......
  • P2198 题解
    解题思路激光塔一定在最后。\(f_{i,j}\)表示前\(i\)个位置放\(j\)(\(j\lei\))个放射塔,那么\(i-j\)个干扰塔的伤害。若第\(i\)个位置放放射塔:\(f_{i,j}=f_{i-1,j-1}+(j-1)\timesg\times[t+b\times(i-j)]\)若第\(i\)个位置放干扰塔,也就是\(j<i\):\(f_{i,j}=\max\{f_{i-......
  • P7974 题解
    解题思路首先可以确保每一次列的方向一定不会与\(s\)到\(t\)的方向相反。不妨设\(l=\min\{s,t\}\),\(r=\max\{s,t\}\)。对于每次移动,所花体力值如下:显然,从\(l\)到\(r\),一定要翻过\([l,r]\)间最高的一个,区间最大我们毫不犹豫地选择ST表,然后我们思考一下,令\(h_x=\m......
  • P4814 题解
    解题思路对于每条边\((u,v)\),权值为\(w\),假设存在一条经过这一条边的路径,其最短距离为\(a\)到\(u\)的最短路加上\(v\)到\(b\)的最短距离加上\(w\),若这个值都大于\(d\),则不可能关闭这条边。由于边权非负,所以可采用dijkstra来处理最短路。因为为有向边,所以可以再建......
  • C++常见入门题题解
    前言因为本人目前比较菜,所以给出的题解都是按照自己的学习进度来的,所以难度是一个循序渐进的过程,由于本人水平有限,望读者能够指出谬误,共同进步。回文数输出#include<bits/stdc++.h>//万能头usingnamespacestd;intmain(void){vector<int>font;//定义一个整型的向......