【CF486E】LIS of Sequence题解

题目链接

题意:

给你一个长度为n的序列a1,a2,...,an,你需要把这n个元素分成三类:1,2,3:

1:所有的最长上升子序列都不包含这个元素

2:有但非所有的最长上升子序列包含这个元素

3:所有的最长上升子序列都包含这个元素

输入格式:

第一行包含一个正整数n,表示序列的长度。第二行包含n个正整数a1,a2,...,an,表示序列中的元素。

输出格式:

一行,包含一个长度为n的、由1,2,3三种数字组成的字符串,第i个数字表示ai所属类别。

样例输入1:


样例输出1:


样例输入2:

   

样例输出2:


样例输入3:

   

样例输出3:


时空限制:

每个测试点2s,256MB

数据范围:

1≤n≤105

1≤ai≤105

题解:

首先,O(nlogn)求LIS大家应该都会吧……这里就不阐述了。

我们可以通过DP来算出以每个点i结尾的最长上升子序列的长度,记为length[i],把整个序列的LIS长度记为len。

然后,大家要想明白一个命题:

如果元素i可能出现在LIS中,那么它在LIS中的位置一定刚好等于length[i]。

这个自证不难。

其次,我们可以用一个“分层数组”,其实所谓的“分层数组”就是一个二维数组,只不过我习惯把第一维叫做“层”罢了,第二维因为节省空间,我们开一个vector。

这个“分层数组”相当重要,它是我们解决问题的核心。我们在DP时就已经算出每个元素的length[i]了,因此我们把这个元素的编号添加到层数为length[i]的vector中去,因此“分层数组”中每一层的每个点就和序列中的元素一一对应。

这就是“分层数组”的建立过程,可参见代码注释。

然后呢?

大家又需要想明白两个命题:

在每一层中,随着编号的递增,与点对应的元素的值单调不递增。

每一层(除第一层外)中的每个点一定在前一层中有编号小于它且序列元素值小于它的节点。

考虑我们DP的过程,我们在更新dp数组中下标为length[i]的最小值时,如果已经通过二分查找算出该元素的length[i],元素可以更新dp[length[i]]的条件是元素的值一定不能比dp[length[i]]大,而dp[length[i]]已经通过前面的元素算出,因此在“分层数组”层数为length[i]的层中,该元素的值一定小于等于在该层中编号比它小的元素的值。

另外,我们在DP时,依据dp数组单调递增的特性可以理解dp[length[i]-1]的值一定比a[i]小,因此在“分层数组”的前一层中,一定有编号小于它且序列元素值小于它的点,这样保证我们在逐层往前推的时候不会出现“一对空”的情况,而是在前一层中是有一个区间(包含编号、序列元素值两方面的限制)与之对应。这个区间中的点就是当前点在前一层中可以扩展到的节点。

大家可能有疑问:为什么要倒着从后往前推呢?

因为倒着推能够保证要扩展的当前点一定有LIS经过它,因此它扩展到的前一层的节点也一定有LIS经过,等到该层所有点扩展完毕后,前一层没有被扩展到的点一定没有LIS经过,打上“1”类标记,下次扩展就不要扩展这些点了。而其他的点都打上“2”类标记,如果只有一个“2”类标记,那么把“2”改成“3”,说明该节点是所有LIS的必经节点。

参考代码如下(若有不理解的参看注释):

 #include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 100005
using namespace std;
int n,a[maxn];//整个序列
int dp[maxn];//dp[i]代表当前长度为i的上升子序列末尾元素的最小值
int len=;//整个序列LIS的长度
int sign[maxn];//序列元素所属类别
vector<int>layer[maxn];//分层数组,一共有len层。layer[i]中的点表示这些元素:以这些元素结尾的最长上升子序列长度为i
inline int bins(int i,int val){
int l=,r=layer[i].size()-;
while(l<r){
int m=(l+r)>>;
if(a[layer[i][m]]>=val)l=m+;
else r=m;
}
return l;
}
int main(){
scanf("%d",&n);
memset(dp,0x3f,sizeof(dp));
dp[]=;
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
int length=lower_bound(dp,dp+n+,a[i])-dp;//O(nlogn)求LIS
dp[length]=min(dp[length],a[i]);
//length表示以该元素结尾的最长上升子序列的长度
layer[length].push_back(i);//加入到分层数组
len=max(len,length);//更新整个序列的LIS长度
}
for(int i=;i<=n;i++)sign[i]=;//初始化全都为第1类,即任何LIS都不经过它们
if(layer[len].size()==)sign[layer[len][]]=;
else for(int i=;i<layer[len].size();i++)sign[layer[len][i]]=;
for(int i=len;i>=;i--){//倒序处理分层数组,一层一层往前推
for(int j=;j<layer[i].size();j++){//枚举当前层的所有点
int bh=layer[i][j];//点的编号
if(sign[bh]>){//如果当前节点可以向前扩展(存在LIS经过当前点)
int l=bins(i-,a[bh]);//二分查找,扩展的节点在序列中的值必须小于当前节点,才能保证LIS严格递增
int r=lower_bound(layer[i-].begin(),layer[i-].end(),bh)-layer[i-].begin()-;//二分查找,扩展的点编号必须小于当前点编号,才能是“序列”
//当前点可扩展到的前一层的点的范围是区间[l,r]
for(int k=l;k<=r;k++)sign[layer[i-][k]]=;//打上标记,该节点能够被扩展到说明一定在整个序列中有某个LIS包含该点
}
}
//当前层能够扩展到的前一层的点是当前层所有点能扩展到的前一层的节点的并集
int cnt=,pos=;
for(int j=;j<layer[i-].size();j++)if(sign[layer[i-][j]]==){
cnt++;
pos=j;
}
if(cnt==)sign[layer[i-][pos]]=;//如果该层所有可扩展的点只能在前一层中扩展出一个节点,说明这个节点是所有LIS的必经节点。
}
for(int i=;i<=n;i++)printf("%d",sign[i]);//不留空格打印
return ;
}

【CF486E】LIS of Sequence题解的更多相关文章

  1. Codeforces 486E LIS of Sequence 题解

    题目大意: 一个序列,问其中每一个元素是否为所有最长上升子序列中的元素或是几个但不是所有最长上升子序列中的元素或一个最长上升子序列都不是. 思路: 求以每一个元素为开头和结尾的最长上升子序列长度,若两 ...

  2. Codeforces Round #277 (Div. 2) E. LIS of Sequence DP

    E. LIS of Sequence Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/486/pr ...

  3. Codeforces 486E LIS of Sequence

    LIS of Sequence 我们先找出那些肯定不会再LIS里面. 然后我们从前往后扫一次, 当前位置为 i , 看存不存在一个 j 会在lis上并且a[ j ] > a[ i ], 如果满足 ...

  4. Codeforces 486E LIS of Sequence(线段树+LIS)

    题目链接:Codeforces 486E LIS of Sequence 题目大意:给定一个数组.如今要确定每一个位置上的数属于哪一种类型. 解题思路:先求出每一个位置选的情况下的最长LIS,由于開始 ...

  5. codeforces 486 E. LIS of Sequence(dp)

    题目链接:http://codeforces.com/contest/486/problem/E 题意:给出n个数,如果一个数满足不属于最长递增序列,那么输出1,如果属于最长递增序列但是不属于所有最长 ...

  6. CF3D Least Cost Bracket Sequence 题解

    题目 This is yet another problem on regular bracket sequences. A bracket sequence is called regular, i ...

  7. Codeforces 486E LIS of Sequence --树状数组求LIS

    题意: 一个序列可能有多个最长子序列,现在问每个元素是以下三个种类的哪一类: 1.不属于任何一个最长子序列 2.属于其中某些但不是全部最长子序列 3.属于全部最长子序列 解法: 我们先求出dp1[i] ...

  8. 486E - LIS of Sequence(LIS)

    题意:给一个长度为n的序列.问每一个数关于序列的LIS(longest increasing subsequence)是什么角色. 这里分了三种: 1.此数没有出如今随意一条LIS中 2.此数出如今至 ...

  9. QAQ的LIS树 QAQ的LIS树2 题解报告

    这两道题实际上考试的时候是一道题OwO 太可怕了,忙了我三个多小时,写了整整7K 这个题两个询问关联性不强,所以分开来考虑 QAQ的LIS树 考虑如何用dp求解答案 设dp(v)表示v到根的修改后的序 ...

随机推荐

  1. JS高级总结

    网址:https://www.cnblogs.com/signheart/p/d6c229a5a758ee1dc21ad5ca2042ab8f.html 通常,通过 JavaScript,您需要操作 ...

  2. 如何利用webpack4.0搭建一个vue项目

    作为一个初学者,记录自己踩过的坑是个好的习惯.我本身比较懒,这里刚好有时间把自己的搭建过程记录一下这里是参考文章   https://www.jianshu.com/p/1fc5b5151abf文章里 ...

  3. spark on yarn 内存分配

    Spark On YARN内存分配 本文主要了解Spark On YARN部署模式下的内存分配情况,因为没有深入研究Spark的源代码,所以只能根据日志去看相关的源代码,从而了解“为什么会这样,为什么 ...

  4. Java相关查询记录

    Version of Spring Facet could not be detected. http://yijiesuifeng.iteye.com/blog/2221444

  5. 第一次博客作业 <西北师范大学| 周安伟>

     1.助教博客链接:https://home.cnblogs.com/u/zaw-315/ 2.本周点评的作业数:3份,有留言互动. 3.本周点评有困难的地方: https://www.cnblogs ...

  6. Android Toast 工具类

    android  中常用系统吐司工具类 package cn.yhq.utils; import android.content.Context; import android.widget.Toas ...

  7. 20175126《Java程序设计》第七周学习总结

    # 20175126 2016-2017-2 <Java程序设计>第七周学习总结 ## 教材学习内容总结 - 本周学习方式主要为手动敲代码并理解内容学习. - 学习内容为教材第八章,本章主 ...

  8. redis学习笔记01 — 基本介绍、安装配置及常用命令

    redis--NoSQL的一种 为了解决高并发.高可用.高扩展.大数据存储等一系列问题而产生的数据库解决方案,就是NoSQL NoSQL,非关系型数据库,全名:Not Only Sql,它不能代替关系 ...

  9. log4j2.xml日志文件设置文件路径

    笔者最近的项目里使用了spring,spring通过web.xml配置监听器,在web启动时web.root系统变量,以供其他变量使用,例如 在属性文件里使用${web.root}以取得完整路径,项目 ...

  10. 查看linux是否为虚拟机,以及其它信息,cpu,主机型号,主板型号等

    dmidecode -s system-product-name 物理机: [root@swnode1]# dmidecode -s system-product-name I840-GS 虚拟机: ...