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 题意: 中文题诶~ 思路: 直接暴力的话时间复杂度为 ...
随机推荐
- 图解 HTTP 连接管理
熟悉我的小伙伴都知道,我之前肝了本<HTTP 核心总结>的 PDF,这本 PDF 是取自我 HTTP 系列文章的汇总,然而我写的 HTTP 相关内容都是一年前了,我回头看了一下这本 PDF ...
- python安全编程之指纹识别
什么是cms CMS是Content Management System的缩写,意为"内容管理系统",这是百度百科的解释,意思是相当于网站的建站模板,整个网站架构已经集成好了,只需 ...
- 给每个li延时添加样式动画效果(setInterval,clearInterval)
btnsAnime($('ul li')) function btnsAnime(pagesl) { var that = this $(pagesl).hide() let i = 0; funct ...
- UVa11054 Gergovia的酒交易(数学归纳法)
直线上有\(n\)个等距村庄,每个村庄要么买酒,要么卖酒.设第\(i\)个村庄对酒的需求为\(A_i\)(\(-1000 \leqslant A_i \leqslant 1000\)),其中\(A_i ...
- Kong网关安装之Docker版(1)
前言: Kong 是天生的微服务网关.她的官方简介是:Kong 是一个云原生,高效,可扩展的分布式 API 网关. 自 2015 年在 github 开源后,广泛受到关注,目前已收获 1.9w+ 的 ...
- [数据结构]ODT(珂朵莉树)实现及其应用,带图
[数据结构]ODT(珂朵莉树)实现及其应用,带图 本文只发布于博客园,其他地方若出现本文均是盗版 算法引入 需要一种这样的数据结构,需要支持区间的修改,区间不同值的分别操作. 一般的,我们会想到用线段 ...
- Linux性能优化-平均负载
Linux性能优化-平均负载 目录 Linux性能优化-平均负载 平均负载的含义 平均负载为多少时合理 平均负载与 CPU 使用率 平均负载案例分析 场景一:CPU 密集型进程 场景二:I/O 密集型 ...
- Java GC【笔记】
Java GC(垃圾回收机制) (PS:篇幅很长的,样式都会重新整一下,比如使用二级) 首先我们得知道,一个对象被判定为垃圾的标准是什么? 对于Java对象来说,当不被其他的对象引用的时候,就可以看作 ...
- SpringBoot开发四-MyBatis入门
需求介绍-MyBatis入门 首先就是安装Mysql Server 和Mysql Workbench. SqlSessionFactory:用于创建SqlSession的工厂类 SqlSession: ...
- JavaWeb单体项目的分层设计与实现
1.概述 为什么要把一个完整的项目(Project)按层拆分成多个模块(Module)? 1)使项目层次更加的清晰: 2)提高代码的复用性: 3)细化分工: 4)解耦. 是不是听起来很高大尚,今天就简 ...