【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. mybatis动态sql之foreach标签

    foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量.它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符.这个元素是很 ...

  2. 【题解二连发】Construct Binary Tree from Inorder and Postorder Traversal & Construct Binary Tree from Preorder and Inorder Traversal

    LeetCode 原题链接 Construct Binary Tree from Inorder and Postorder Traversal - LeetCode Construct Binary ...

  3. PostgreSQL 锁等待诊断详解

    摘要PostgreSQL和大多数传统RDBMS一样,都设计了大量的锁来保证并发操作的数据一致性. 同时PG在设计锁等待时,以队列方式存储等待锁. 参考 ProcSleep()@src/backend/ ...

  4. 【c】多级指针

    一.一级指针 1.int *p,*p2; p是变量名,*表明是指针,指针指向地址. 在定义时初始化,如int *p_2 = &b; //定义一个指针,指针指向一个地址 先定义再初始化,如int ...

  5. 19-03【golang】strings包

    golang的strings包提供了字符串操作的一系列函数.下面做个简单介绍 函数 用法 备注 Compare(a,b sring) 比较两个字符串   Contains(s, substr stri ...

  6. 内网ssh穿透

    公司服务器没有公网IP,只有内网IP,利用自己的阿里云服务器(有公网ip)做ssh内网穿透,使得外网可访问.方法如下: 环境: 公司服务器和阿里云服务器均为 Ubuntu 操作系统, 需要修改阿里云服 ...

  7. hibernate的理解

    emm~这里就是记录一下,hibernate的save,如果存在id,就可以直接save,就会覆盖原有的,如果不存在id就会在数据库创建一条新的记录 package edu.zut.cs.zutnlp ...

  8. Echart遇到的问题:tooltip提示框大小异常

    将Echart图表放到项目中,发现当鼠标放到柱状图上时,提示框显示大小超出了div的大小. 官方的文档对于tooltip的说明也没有指出:如何修改大小. 那么tooltip是什么呢? fn+f12打开 ...

  9. PEP8 规范

    Python PEP8 编码规范中文版   原文链接:http://legacy.python.org/dev/peps/pep-0008/ item detail PEP 8 Title Style ...

  10. C#使用反射获取对象变化的情况

    记录日志时, 经常需要描述对象的状态发生了怎样的变化, 以前处理的非常简单粗暴: a. 重写class的ToString()方法, 将重要的属性都输出来 b. 记录日志时:  谁谁谁  由  变更前实 ...