前言:

  这是一篇杂题选讲+作者口胡的博客,不喜勿喷。

正文:

  提示:在阅读时请留意加粗的字体是“极长”还是“最长”。

  今天改题时碰到了一道关于线段树维护单调栈求极大上升子序列的题,没看出来,懵了一整个下午,感到对这个知识点不熟,所以来总结一下。

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(上升子序列)的更多相关文章

  1. 用python实现最长公共子序列算法(找到所有最长公共子串)

    软件安全的一个小实验,正好复习一下LCS的写法. 实现LCS的算法和算法导论上的方式基本一致,都是先建好两个表,一个存储在(i,j)处当前最长公共子序列长度,另一个存储在(i,j)处的回溯方向. 相对 ...

  2. codevs 1576 最长上升子序列的线段树优化

    题目:codevs 1576 最长严格上升子序列 链接:http://codevs.cn/problem/1576/ 优化的地方是 1到i-1 中最大的 f[j]值,并且A[j]<A[i] .根 ...

  3. [LeetCode] Arithmetic Slices II - Subsequence 算数切片之二 - 子序列

    A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...

  4. [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 ...

  5. [LeetCode] Wiggle Subsequence 摆动子序列

    A sequence of numbers is called a wiggle sequence if the differences between successive numbers stri ...

  6. [LeetCode] Increasing Triplet Subsequence 递增的三元子序列

    Given an unsorted array return whether an increasing subsequence of length 3 exists or not in the ar ...

  7. [LeetCode] Distinct Subsequences 不同的子序列

    Given a string S and a string T, count the number of distinct subsequences of T in S. A subsequence ...

  8. 动态规划之最长公共子序列(LCS)

    转自:http://segmentfault.com/blog/exploring/ LCS 问题描述 定义: 一个数列 S,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则 ...

  9. [Data Structure] LCSs——最长公共子序列和最长公共子串

    1. 什么是 LCSs? 什么是 LCSs? 好多博友看到这几个字母可能比较困惑,因为这是我自己对两个常见问题的统称,它们分别为最长公共子序列问题(Longest-Common-Subsequence ...

  10. 51nod1134(最长递增子序列)

    题目链接: https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1134 题意: 中文题诶~ 思路: 直接暴力的话时间复杂度为 ...

随机推荐

  1. phpcms开发使用

    二次开发入口文件: 1.dirname(__FILE___) 函数返回的是脚本所在在的路径 2.__FILE__ 当前运行文件的完整路径和文件名.如果用在被包含文件中,则返回被包含的文件名. 3.DI ...

  2. 适合普通大学生的 Java 后端开发学习路线

    大家好,我是帅地. 接下来的一段时间,帅地会总结各种技术栈的学习路线,例如 Java 开发,C++ 开发,python 开发,前端开发等等,假如你没有明确的目标,或许可以按照我说的学习路线来学习一波, ...

  3. ClickHouse入门笔记

    ClickHouse笔记 目录 ClickHouse笔记 第 1 章 ClickHouse 入门 列式储存的好处: 第 2 章 ClickHouse 的安装 第 3 章 数据类型 整型 浮点型 布尔型 ...

  4. SQL Server存储过程执行一个带返回值(output)的存储过程

    语法如下: --存储过程一CREATE PROCEDURE testProc @out VARCHAR(500) OUTPUT AS BEGIN SET @out='操作成功' RETURNEND - ...

  5. Android面试中多说这么一句话,薪水直接涨5k

    小鱼同学最近很难受,明明获得了人人羡慕的大厂Offer,走在路上都能被熟人祝贺,学弟学妹争着抢着求他进行经验分享. 但有件事一直让他很介意.其实这件事起因很简单,是关于这份Offer的薪资问题,面试的 ...

  6. Spring Security中实现微信网页授权

    微信公众号提供了微信支付.微信优惠券.微信H5红包.微信红包封面等等促销工具来帮助我们的应用拉新保活.但是这些福利要想正确地发放到用户的手里就必须拿到用户特定的(微信应用)微信标识openid甚至是用 ...

  7. Create Virtual Network with VirtualBox on Mint 14

    VirtualBox version: VirtualBox-4.2.18-88780-Linux_x86.run Host OS: Linux Mint 14 Xfce Setup Network ...

  8. HDFS 09 - HDFS NameNode 的高可用机制

    目录 1 - 为什么要高可用 2 - NameNode 的高可用发展史 3 - HDFS 的高可用架构 3.1 Standby 和 Active 的命名空间保持一致 3.2 同一时刻只有一个 Acti ...

  9. websocket在慕课网中的应用

    网上资料都是介绍概念,我们来看看实际网站怎么使用websocket的吧.限于自身水平解读并不深入,慕课网上的websocket某些字段不知何用. 是什么 是一种应用层协议,有html5而推出,是一种全 ...

  10. SpringBoot开启异步方法

    在启动类上加入@EnableAsync 异步方法 /** * 简单文本邮件 * @param to 收件人 * @param subject 主题 * @param content 内容 */ @As ...