【CF486E】LIS of Sequence题解
【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题解的更多相关文章
- Codeforces 486E LIS of Sequence 题解
题目大意: 一个序列,问其中每一个元素是否为所有最长上升子序列中的元素或是几个但不是所有最长上升子序列中的元素或一个最长上升子序列都不是. 思路: 求以每一个元素为开头和结尾的最长上升子序列长度,若两 ...
- 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 ...
- Codeforces 486E LIS of Sequence
LIS of Sequence 我们先找出那些肯定不会再LIS里面. 然后我们从前往后扫一次, 当前位置为 i , 看存不存在一个 j 会在lis上并且a[ j ] > a[ i ], 如果满足 ...
- Codeforces 486E LIS of Sequence(线段树+LIS)
题目链接:Codeforces 486E LIS of Sequence 题目大意:给定一个数组.如今要确定每一个位置上的数属于哪一种类型. 解题思路:先求出每一个位置选的情况下的最长LIS,由于開始 ...
- codeforces 486 E. LIS of Sequence(dp)
题目链接:http://codeforces.com/contest/486/problem/E 题意:给出n个数,如果一个数满足不属于最长递增序列,那么输出1,如果属于最长递增序列但是不属于所有最长 ...
- CF3D Least Cost Bracket Sequence 题解
题目 This is yet another problem on regular bracket sequences. A bracket sequence is called regular, i ...
- Codeforces 486E LIS of Sequence --树状数组求LIS
题意: 一个序列可能有多个最长子序列,现在问每个元素是以下三个种类的哪一类: 1.不属于任何一个最长子序列 2.属于其中某些但不是全部最长子序列 3.属于全部最长子序列 解法: 我们先求出dp1[i] ...
- 486E - LIS of Sequence(LIS)
题意:给一个长度为n的序列.问每一个数关于序列的LIS(longest increasing subsequence)是什么角色. 这里分了三种: 1.此数没有出如今随意一条LIS中 2.此数出如今至 ...
- QAQ的LIS树 QAQ的LIS树2 题解报告
这两道题实际上考试的时候是一道题OwO 太可怕了,忙了我三个多小时,写了整整7K 这个题两个询问关联性不强,所以分开来考虑 QAQ的LIS树 考虑如何用dp求解答案 设dp(v)表示v到根的修改后的序 ...
随机推荐
- mui-H5获取当前手机通讯录
mui.plusReady(function() { // 扩展API加载完毕,现在可以正常调用扩展API plus.contacts.getAddressBook(plus.contacts.ADD ...
- pytorch之张量的理解
张量==容器 张量是现代机器学习的基础,他的核心是一个容器,多数情况下,它包含数字,因此可以将它看成一个数字的水桶. 张量有很多中形式,首先让我们来看最基本的形式.从0维到5维的形式 0维张量/标量: ...
- 《Orange‘s》FAT12文件系统
FAT12 层次 扇区(Sector):磁盘上的最小数据单元 簇(Cluster):一个或多个扇区 分区(Partition):通常指整个文件系统 引导扇区 引导扇区是整块软盘的第0个扇区,在这个扇区 ...
- 通过ajax返回值
通过ajax获取数据 然后使用 在console.log可以得到值 但是 返回值的在另一个ajax却没有结果为空 百度了一下 才发现少加了这句话 这样同步就可以正常使用了
- 【JavaScript】EasyUIのForm的跨域提交问题解析
昨日.プログラムを作るとき.一つの問題がありますが.皆に共有します. [問題] EasyUIのFormでURLを請求するとき.返却の値が取得できない. [ソース] var fnRegUser = fu ...
- windows10安装JIRA
windows10安装MySQL数据库 一.问题现象: cmd执行“mysql”命令,提示:ERROR 2003 (HY000): Can't connect to MySQL server on ' ...
- Add `gem 'sqlite3'` to your Gemfile
错误:Specified 'sqlite3' for database adapter, but the gem is not loaded. Add `gem 'sqlite3'` to your ...
- sql yog出现2013错误
首先用telnet检查一下你的防火墙端口,如果连接失败,配置防火墙. 配置防火墙,开启3306端口 find -name iptables vim /etc/sysconfig/iptables -A ...
- Java并发编程:深入剖析ThreadLocal(转)
目录大纲: 一.对ThreadLocal的理解 二.深入解析ThreadLocal类 三.ThreadLocal的应用场景 原文链接:http://www.cnblogs.com/dolphin052 ...
- SpringBoot集成MongoDB
前言 之前写了各种nosql数据库的比较,以及相关理论,现在我在本地以springboot+MongoDB框架,探究了具体的运行流程,下面总结一下,分享给大家. 运行前准备 安装并启动MongoDB应 ...