IS(上升子序列)
前言:
这是一篇杂题选讲+作者口胡的博客,不喜勿喷。
正文:
提示:在阅读时请留意加粗的字体是“极长”还是“最长”。
今天改题时碰到了一道关于线段树维护单调栈求极大上升子序列的题,没看出来,懵了一整个下午,感到对这个知识点不熟,所以来总结一下。
What is IS??
其实我是将LIS删了一个L造的这个词,。所谓上升子序列,就是在原序列中抽出一个子序列(不一定连续),他满足任意\(i<j\)有\(v[i]<v[j]\)。
最长上升子序列(\(LIS\)):就是\(IS\)里最长的。
极大上升子序列:就是一个不会被其他任意一个\(IS\)包含的\(IS\)。
这两个是常考点,然而我总是看不出来。
他们的性质:
主要是考虑极长上升子序列,因为最大上升子序列的性质除了最大外就和他的性质一样了。
1.满足任意\(i<j\)有\(v[i]<v[j]\)。
2.在原序列中,极长上升子序列\({p_{1},p_{2},p_{3}......p_{m}}(p_{1}<p_{2}<p_{3}<......<p_{m})\)满足:
不存在一个\(j\),使得\(j<p_{1}\)且\(v[j]<v[p_{1}]\)。
不存在一个\(j\),使得\(j>p_{m}\)且\(v[j]>v[p_{m}]\)。
不存在一个\(j\),使得\(p_{i}<j<p_{i+1}\)且\(v[p_{i}]<v[j]<v[p_{i+1}]\)
第二条尤为重要,考试时就栽在他上面了。。。。。。
如何求??
对于最长上升子序列来说:
传统做法是线性DP。
int c[N];
for(int i=1;i<=n;i++)
{
c[i]=1;
for(int j=1;j<i;j++)
if(v[j]<v[i])
c[i]=max(c[i],c[j]+1);
}
复杂度:\(O(N^{2})\)
有一种线段树优化,叫线段树维护单调栈,可以优化至\(O(Nlog^{2}_{2}N)\)。
int c[N];
#define lc id<<1
#define rc id<<1|1
class Line_tree
{
private:
int maxn[N<<2];
public:
void insert(int,int,int,int,int);
int query(int,int,int,int,int);
}t;
void Line_tree::insert(int id,int l,int r,int pos,int val)
{
if(l==r){maxn[id]=val;return;}
int mid=(l+r)>>1;
if(pos<=mid) insert(lc,l,mid,pos,val);
else insert(rc,mid+1,r,pos,val);
maxn[id]=max(maxn[lc],maxn[rc]);
}
int Line_tree::query(int id,int l,int r,int st,int en)
{
if(st<=l&&r<=en) return maxn[id];
int mid=(l+r)>>1;
int ret=-inf;
if(st<=mid) ret=max(ret,query(lc,l,mid,st,en));
if(mid<en) ret=max(ret,query(rc,mid+1,r,st,en));
return ret;
}
#undef lc
#undef rc
//在主函数里:
for(int i=1;i<=n;i++)
{
int id=0;
if(v[i]-1>=1)
id=t.query(1,1,n,1,v[i]-1);
while(id)
{
c[i]=max(c[i],c[id]+1);
if(v[id]+1>v[i]-1)
break;
id=t.query(1,1,n,v[id]+1,v[i]-1);
}
ans=max(ans,c[i]);
}
其实它主要的用途是做一些与极大上升子序列有关的统计时用的,比如方案数统计、最小权极大上升子序列等等。。。并不推荐用来单纯求序列,因为求序列的话他的复杂度不如下者。
还有一种优化,是\(O(Nlog_{2}N)\)的,他就是主要用来求最大序列及方案数了。
#include<bits/stdc++.h>
using namespace std;
namespace STD
{
#define rr register
#define inf INT_MAX
typedef long long ll;
const int N=100004;
const int mod=123456789;
const int R=100003;
int n,type;
int r[N];
#define lc id<<1
#define rc id<<1|1
struct node
{
int maxn;
ll cnt;
node(){maxn=cnt=0;}
node(int maxn_,ll cnt_){maxn=maxn_,cnt=cnt_;}
};
class Line_tree
{
private:
node a[R<<2];
void Insert(int,int,int,int,node);
node Query(int,int,int,int,int);
public:
Line_tree(){}
void insert(int pos,node val){Insert(1,1,R-1,pos,val);}
node query(int st,int en)
{
if(st>en) return node(0,0);
return Query(1,1,R-1,st,en);
}
}t;
void Line_tree::Insert(int id,int l,int r,int pos,node val)
{
if(l==r)
{
if(a[id].maxn==val.maxn)
a[id].cnt=(a[id].cnt+val.cnt)%mod;
else if(a[id].maxn<val.maxn)
a[id]=val;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) Insert(lc,l,mid,pos,val);
else Insert(rc,mid+1,r,pos,val);
a[id]=(a[lc].maxn>a[rc].maxn)?a[lc]:a[rc];
if(a[lc].maxn==a[rc].maxn)
a[id].cnt=(a[lc].cnt+a[rc].cnt)%mod;
}
node Line_tree::Query(int id,int l,int r,int st,int en)
{
if(st<=l&&r<=en) return a[id];
int mid=(l+r)>>1;
node ret=node(-inf,0),temp;
if(st<=mid)
{
temp=Query(lc,l,mid,st,en);
if(temp.maxn>ret.maxn)
ret=temp;
else if(temp.maxn==ret.maxn)
ret.cnt=(ret.cnt+temp.cnt)%mod;
}
if(mid<en)
{
temp=Query(rc,mid+1,r,st,en);
if(temp.maxn>ret.maxn)
ret=temp;
else if(temp.maxn==ret.maxn)
ret.cnt=(ret.cnt+temp.cnt)%mod;
}
return ret;
}
#undef lc
#undef rc
int read()
{
rr int x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
};
using namespace STD;
int main()
{
n=read(),type=read();
for(rr int i=1;i<=n;i++)
r[i]=read();
int ans=-inf;
ll num=0;
for(rr int i=1;i<=n;i++)
{
node temp=t.query(1,r[i]-1);
temp.maxn++;
if(temp.cnt==0)
temp.cnt=1;
if(ans<temp.maxn)
{
num=temp.cnt;
ans=temp.maxn;
}
else if(ans==temp.maxn)
num=(num+temp.cnt)%mod;
t.insert(r[i],temp);
}
cout<<ans;
if(type==1)
cout<<num;
}
其实这两种方法主要的区别是:
前者通过那个枚举id的循环枚举i之前已有的极长序列,以此为算法框架,来进行一些额外的维护,他可以知道具体的方案,即对应下标,有了下标,就可以以此为框架维护线段树之外的东西了。
后者线段树维护的是长度,因此只能局限于对长度的求解,稍带着维护方案数,他无法知道具体方案即下标,局限性相对大。
另外,在统计极长子序列的方案总数时,可以直接从后往前跳单调不降序列然后累加结果即可,就像这样:
//在这之前你已经求好了方案数
int x=-inf;//随便一个极小值
for(int i=n;i>=1;i--)
{
if(v[i]>=x)
{
x=v[i];
ans+=c[i];
}
}
2021-07-29 21:30:08 星期四 现役
IS(上升子序列)的更多相关文章
- 用python实现最长公共子序列算法(找到所有最长公共子串)
软件安全的一个小实验,正好复习一下LCS的写法. 实现LCS的算法和算法导论上的方式基本一致,都是先建好两个表,一个存储在(i,j)处当前最长公共子序列长度,另一个存储在(i,j)处的回溯方向. 相对 ...
- codevs 1576 最长上升子序列的线段树优化
题目:codevs 1576 最长严格上升子序列 链接:http://codevs.cn/problem/1576/ 优化的地方是 1到i-1 中最大的 f[j]值,并且A[j]<A[i] .根 ...
- [LeetCode] Arithmetic Slices II - Subsequence 算数切片之二 - 子序列
A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...
- [LeetCode] Is Subsequence 是子序列
Given a string s and a string t, check if s is subsequence of t. You may assume that there is only l ...
- [LeetCode] Wiggle Subsequence 摆动子序列
A sequence of numbers is called a wiggle sequence if the differences between successive numbers stri ...
- [LeetCode] Increasing Triplet Subsequence 递增的三元子序列
Given an unsorted array return whether an increasing subsequence of length 3 exists or not in the ar ...
- [LeetCode] Distinct Subsequences 不同的子序列
Given a string S and a string T, count the number of distinct subsequences of T in S. A subsequence ...
- 动态规划之最长公共子序列(LCS)
转自:http://segmentfault.com/blog/exploring/ LCS 问题描述 定义: 一个数列 S,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则 ...
- [Data Structure] LCSs——最长公共子序列和最长公共子串
1. 什么是 LCSs? 什么是 LCSs? 好多博友看到这几个字母可能比较困惑,因为这是我自己对两个常见问题的统称,它们分别为最长公共子序列问题(Longest-Common-Subsequence ...
- 51nod1134(最长递增子序列)
题目链接: https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1134 题意: 中文题诶~ 思路: 直接暴力的话时间复杂度为 ...
随机推荐
- 【LeetCode】297. 二叉树的序列化与反序列化
297. 二叉树的序列化与反序列化 知识点:二叉树:递归 题目描述 序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一 ...
- 8.3考试总结(NOIP模拟19)[最长不下降子序列·完全背包问题·最近公共祖先]
一定要保护自己的梦想,即使牺牲一切. 前言 把人给考没了... 看出来 T1 是一个周期性的东西了,先是打了一个暴力,想着打完 T2 T3 暴力就回来打.. 然后,就看着 T2 上头了,后来发现是看错 ...
- 媒体应用视频超分AI神器!360P视频一键转换HD
作为多媒体应用的开发者,你是否想为媒体播放器快速开发创新AI功能?例如: 在播放低画质视频过程中对其进行逐帧超分 让满屏飘飞的弹幕自动绕过画面的主体人物 HMS Core 6.0.0开放的多媒体管线服 ...
- Hadoop 3.1.1 - Yarn - 使用 CGroups
在 Yarn 上使用 CGroups CGroups 是一种将任务及其子任务聚集和划分进一个垂直的分组的策略,并提供在此结构上的特别的操作.CGroups 是 Linux 内核功能,自内核版本 2.6 ...
- shell $? 状态码含义
Linux 使用了$? 来保存上个执行的命令的退出状态码. 0 命令成功结束 1 通用未知错误 2 误用she ...
- Linux各目录及每个目录的详细介绍总结
Linux各目录及每个目录的详细介绍 [常见目录说明] 目录 /bin 存放二进制可执行文件(ls,cat,mkdir等),常用命令一般都在这里. /etc 存放系统管理和配置文件 /home 存放所 ...
- 学习vue过程中遇到的问题
1.vue-quill-editor动态禁用 项目中把vue-quill-editor单独封装成了一个组件,通过props传递readOnly参数来设置是否禁用editor.开发中发现可以实现禁用效果 ...
- VLAN-5 利用三层交换机实现vlan间的路由
一.实验拓扑图 二.实验编址 三.实验步骤 1.给对应的PC设置对应的IP和掩码还有接口,以及根据需要划分不同的vlan区域,再用文本标记出不同部门. 2.启动设备(全选) 3.首先用ping命令检查 ...
- tomcat下载、安装及配置
一,下载Tomcat 1.进入官网Http://tomcat.apache.org/,选择download,下载所需要的Tomcat版本. 注意有zip和exe两种格式的 zip(64-bit Win ...
- IP实验笔记
代码: 对LSW1: Vlan 10 Interface ethernet 0/0/1 Port link-type access Port default vlan 10 Interface eth ...