前言:

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

正文:

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

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

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. SAS 常用字符串函数

    原文链接:https://www.cnblogs.com/snoopy1866/p/15085466.html CAT(item-1 <, -, item-n>) : 在保留首尾空格的情况 ...

  2. springboot未授权

    http://ip/actuator/heapdump http://ip/env http://ip/autoconfig http://ip/info http://ip/trace

  3. 记一次mysql事务未提交导致锁未释放的问题

    记一次mysql事务未提交导致锁未释放的问题 ## 查看未提交的事务(3秒内未操作的事务) SELECT p.ID AS conn_id, P.USER AS login_user, P.HOST A ...

  4. 关于修改.net core webapi中null默认返回的状态码。

    在asp .net core webapi中,http请求的响应数据如果是null的话,我们知道状态码会返回204,即NoContent,为什么会出现这种情况呢?   因为在返回响应数据的时候,nul ...

  5. 授予mysql的其他用户数据库的使用权限

    场景:不同的开发人员有不同的数据库的权限:也可适用于外包公司不同的开发权限. root用户登录数据库,命令行执行下面语句即可. grant select,delete,update,create,dr ...

  6. Nginx配置websocket的安全协议wss

    //nginx配置wss访问方式 map $http_upgrade $connection_upgrade { default upgrade; '' close; } upstream webso ...

  7. docker-02

    环境准备 10.0.0.100这台宿主机先做好给docker容器本地yum源,其实也可以用阿里等其他的yum源 1 上传6.9和7.6的镜像到10.0.0.100这台服务器 [root@docker ...

  8. MySQL-13-日志管理

    常用日志参数 经常用到的有错误.快慢查询.二进制等日志 错误日志 1 作用 记录启动\关闭\日常运行过程中,状态信息,警告,错误,排查MySQL运行过程的故障 2 错误日志配置 默认就是开启的: /数 ...

  9. Java JVM【笔记】

    Java JVM[笔记] Java的平台无关性是如何实现的? Java源码首先被编译成字节码,再由不同的平台的JVM进行解析,Java语言在不同的平台上运行时不需要进行重新编译,Java虚拟机在执行字 ...

  10. Ubuntu本地提权适配不同小版本内核(CVE-2017-16995)

    朋友在执行的时候说有的会出现提权不成功,内核crash掉的现象.因为cred结构体的偏移量可能因为内核版本不同.内核编译选项不同而出现差异,作者给的exp偏移量是写死的,所以exp里面对应的偏移地址也 ...