【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. 使用pycharm 出现 interpreter field is empty 完美解决方法(转载 记录)

    使用pycharm 出现 interpreter field is empty 主要是因为你的电脑没有正确安装python或者安装python出错,重新下载安装覆盖就行 下载安装包:从Python的官 ...

  2. 数据仓库系列 - 缓慢渐变维度 (Slowly Changing Dimension) 常见的三种类型及原型设计

    在从 OLTP 业务数据库向 DW 数据仓库抽取数据的过程中,特别是第一次导入之后的每一次增量抽取往往会遇到这样的问题:业务数据库中的一些数据发生了更改,到底要不要将这些变化也反映到数据仓库中?在数据 ...

  3. Java框架spring 学习笔记(一):SpringBean、ApplicationContext 容器、BeanFactory容器

    Spring容器是Spring框架的核心,容器可以创建对象并创建的对象连接在一起,配置和管理他们的整个生命周期.Spring 容器使用依赖注入(DI)来作为管理应用程序的组件,被称为 Spring B ...

  4. Android 添加第三方jar包

    1,拷贝jar包到项目的\app\libs文件夹下. 2,打开项目下的build.gradle(Module:app)文件,在“dependencies {}”中添加“compile files('l ...

  5. 算法练习LeetCode初级算法之设计问题

    打乱数组 不断的让第一个与后面随机选择的数交换 class Solution { private int[] nums; private int[] initnums; public Solution ...

  6. shell脚本简介

    什么是shell? shell是一个命令解释器,它在操作系统的最外层,负责直接与用户对话,把用户的输入解释给操作系统,并处理各种各样的操作系统的输出结果,输出到屏幕返回给用户.这种对话方式可以是交互的 ...

  7. 20164319 刘蕴哲 Exp2 后门原理与实践

    [后门概念] 后门就是不经过正常认证流程而访问系统的通道. 特指潜伏于操作系统中专门做后门的一个程序,而“坏人”可以连接这个程序远程执行各种指令. (概念和木马有重叠) [学习内容] 使用nc实现wi ...

  8. JavaSE基础知识(5)—面向对象(5.5 this和super关键字)

    一.this关键字 1.说明 this关键字代表当前类的对象,可以访问本类的属性.方法.构造器注意:谁调用该方法,则this就指谁 2.语法 访问属性: this.属性名 = 值; System.ou ...

  9. tablib cell() missing 1 required positional argument: 'column' 报错

    可能是安装版本问题 pip uninstall tablib, 重新安装 pip install tablib==0.11.4. 试一试

  10. Python中__init__和self的意义和作用

    由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去.以学生类为例,通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑 ...