最长上升子序列(LIS)长度及其数量
例题51Nod-1376,一个经典问题,给出一个序列问该序列的LIS以及LIS的数量。
这里我学习了两种解法,思路和代码都是参考这两位大佬的:
https://www.cnblogs.com/reverymoon/p/9496040.html
https://www.cnblogs.com/RabbitHu/archive/2017/11/02/51nod1376.html
首先是先分析一下问题,求LIS是一个很基础的问题了,用得最多的是nlogn的解法这里就不讲了。当我们求出序列a[i]的LIS为f[i]时,我们应该从哪里得到LIS的数量s[i]呢?
显然应该是 s[i]=sigma(s[j])(j<i,a[j]<a[i],f[j]=f[i]-1),即满足这三个条件的s[j]的和。因为我们是从左到右做,所以第一个条件满足,然后问题就是怎么能快速找到a[j]<a[i]且f[j]=f[i]-1的s[j]的和呢?
这里博主学习到两种解法,先将树状数组解法:将离散化后的a[i]作为下标在树状数组上建树,那么此时a[i]的前缀就必定满足a[j]<a[i]了,同时树状数组维护的是以a[i]结尾的LIS长度f[i]和a[i]的LIS数量s[i]。当求解a[i]时,我们在BIT上查询a[i]-1前缀的最长LIS同时累加最长LIS的数量,那么返回的结果就是后面一个数可以接上a[i]这个数的最长LIS和它的数量(这句话是重点)。所以f[i]=返回结果+1,s[i]=返回结果数量。代码实现是学习上面博客大佬,带有一些小技巧。
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+;
const int P=1e9+;
int n,m,a[N],b[N];
struct dat{
int len,sum;
}bit[N],dp[N]; void upd(dat &x,dat y) { //计算y对x的影响
if (x.len>y.len) return;
else if (x.len<y.len) x=y;
else x.sum+=y.sum,x.sum%=P;
} void update(int x,dat v) {
for (;x<=m;x+=x&-x) upd(bit[x],v);
} dat query(int x) {
dat ret=(dat){,};
for (;x;x-=x&-x) upd(ret,bit[x]);
return ret;
} int main()
{
cin>>n;
for (int i=;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
sort(b+,b+n+);
m=unique(b+,b+n+)-(b+);
for (int i=;i<=n;i++) a[i]=lower_bound(b+,b+m+,a[i])-b;
//update(0,(dat){0,1}); 不能这样写会死循环
for (int i=;i<=n;i++) {
dp[i]=query(a[i]-); dp[i].len++;
update(a[i],dp[i]);
}
dat ans=(dat){,};
for (int i=;i<=n;i++) upd(ans,dp[i]);
cout<<ans.sum<<endl;
return ;
}
然后是同一道题的Vector+二分的解法。这种解法会更加容易理解,还是看上面三个条件(j<i,a[j]<a[i],f[j]=f[i]-1),然后第一个条件不用管考虑后两个条件。在学习nlogn的LIS解法的时候应该有学习到:从左到右同等长度的LIS末尾数字应该是单调不增的,基于这个我们可以考虑把前面的a[j]按照f[j]大小分组,同等LIS长度的元素a[j]存在同一个vector里面,然后这个vector的元素应该是单调不增的,那么我们就可以通过二分查找在f[i]-1的这一个vector里面快速地找到所有的<a[i]的元素,同时也就是快速算出满足上诉三个条件的s[j]总和。
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+;
const int P=1e9+;
const int INF=0x3f3f3f3f;
int n,m,a[N],g[N],f[N],s[N];
vector<int> len[N],sum[N]; //len[i]存放LIS为i的数a[i],sum[i]存放a[i]对应的LIS数量
//显然len[i]的元素是单调不增的 int Find(int p,int x) { //二分len=p的最后一个>=x的位置,这个位置后面的就是答案
int l=,r=len[p].size()-,mid;
while (l<r) {
mid=l+r+>>;
if (len[p][mid]>=x) l=mid;
else r=mid-;
}
return ((sum[p].back()-sum[p][l])%P+P)%P;
} int main()
{
cin>>n;
for (int i=;i<=n;i++) scanf("%d",&a[i]);
memset(g,0x3f,sizeof(g));
for (int i=;i<=n;i++) len[i].push_back(INF),sum[i].push_back();
len[].push_back(-INF),sum[].push_back();
int Max=,ans=;
for (int i=;i<=n;i++) {
int t=lower_bound(g+,g+n+,a[i])-g;
g[t]=a[i];
f[i]=t; s[i]=Find(t-,a[i]);
len[t].push_back(a[i]); sum[t].push_back((sum[t].back()+s[i])%P); if (f[i]>Max) Max=f[i],ans=s[i];
else if (f[i]==Max) ans=(ans+s[i])%P;
}
cout<<ans<<endl;
return ;
}
题目练习:
2019计蒜之道 复赛 外教 Michale 变身大熊猫:https://nanti.jisuanke.com/t/39611
容易想到a[i]会在LIS的概率就是:(必须经过a[i]的LIS数量)/(所有的LIS数量) 。所有的LIS数量好求,主要问题就是怎么求必须经过a[i]的LIS的数量,这个问题也不难想,就是 从左到右以a[i]结尾的LIS数量*从右到左为a[i]为结尾的最长下降子序列数量。这就用到上面的知识了。
#include<bits/stdc++.h>
using namespace std;
const int P=;
const int N=5e5+;
typedef long long LL;
int n,m,a[N],b[N],c[N]; struct dat{
int len,sum;
}dp1[N],dp2[N],bit[N];
void upd(dat &x,dat y) {
if (x.len>y.len) return;
else if (x.len<y.len) x=y;
else x.sum+=y.sum,x.sum%=P;
}
void update(int x,dat v) {
for (;x<=m;x+=x&-x) upd(bit[x],v);
}
dat query(int x) {
dat ret=(dat){,};
for (;x;x-=x&-x) upd(ret,bit[x]);
return ret;
} int power(int x,int p) {
int ret=;
for (;p;p>>=) {
if (p&) ret=((LL)ret*x)%P;
x=((LL)x*x)%P;
}
return ret;
} int main()
{
cin>>n;
for (int i=;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
sort(b+,b+n+);
m=unique(b+,b+n+)-(b+);
for (int i=;i<=n;i++) c[i]=lower_bound(b+,b+m+,a[i])-b; dat ans=(dat){,};
memset(bit,,sizeof(bit));
for (int i=;i<=n;i++) {
dp1[i]=query(c[i]-); dp1[i].len++;
update(c[i],dp1[i]);
upd(ans,dp1[i]);
} for (int i=;i<=n;i++) c[i]=m-c[i]+;
memset(bit,,sizeof(bit));
for (int i=n;i;i--) {
dp2[i]=query(c[i]-); dp2[i].len++;
update(c[i],dp2[i]);
} for (int i=;i<=n;i++)
if (dp1[i].len+dp2[i].len-==ans.len) {
int t=(LL)dp1[i].sum*dp2[i].sum%P*power(ans.sum,P-)%P;
printf("%d ",t);
} else printf("0 ");
return ;
}
最长上升子序列(LIS)长度及其数量的更多相关文章
- 最长上升子序列(LIS)长度的O(nlogn)算法
最长上升子序列(LIS)的典型变形,熟悉的n^2的动归会超时.LIS问题可以优化为nlogn的算法.定义d[k]:长度为k的上升子序列的最末元素,若有多个长度为k的上升子序列,则记录最小的那个最末元素 ...
- 动态规划--最长上升子序列(LIS)的长度
l例如:对于[3,1,4,2,5],最长上升子序列的长度是3 arr = [3,1,4,5,9,2,6,5,0] def lis(arr): #dp[i]表示第i个位置的值为尾的数组的最长递增子序列的 ...
- 专题2:最长上升子序列LIS
A HDU 1025 Constructing Roads In JGShining's Kingdom B POJ 3903 Stock Exchange C OpenJ_B ...
- 一个数组求其最长递增子序列(LIS)
一个数组求其最长递增子序列(LIS) 例如数组{3, 1, 4, 2, 3, 9, 4, 6}的LIS是{1, 2, 3, 4, 6},长度为5,假设数组长度为N,求数组的LIS的长度, 需要一个额外 ...
- 最长上升子序列(LIS)模板
最长递增(上升)子序列问题:在一列数中寻找一些数,这些数满足:任意两个数a[i]和a[j],若i<j,必有a[i]<a[j],这样最长的子序列称为最长递增(上升)子序列. 考虑两个数a[x ...
- hdu1025 dp(最长上升子序列LIS)
题意:有一些穷国和一些富国分别排在两条直线上,每个穷国和一个富国之间可以建道路,但是路不能交叉,给出每个穷国和富国的联系,求最多能建多少条路 我一开始在想有点像二分图匹配orz,很快就发现,当我把穷国 ...
- SGU 199 - Beautiful People 最长上升子序列LIS
要邀请n个人参加party,每个人有力量值strength Si和魅力值 beauty Bi,如果存在两人S i ≤ S j and B i ≥ B j 或者 S i ≥ S j and B i ≤ ...
- 最长上升子序列 LIS(Longest Increasing Subsequence)
引出: 问题描述:给出一个序列a1,a2,a3,a4,a5,a6,a7….an,求它的一个子序列(设为s1,s2,…sn),使得这个子序列满足这样的性质,s1<s2<s3<…< ...
- 【CJOJ2498】【DP合集】最长上升子序列 LIS
题面 Description 给出一个 1 ∼ n (n ≤ 10^5) 的排列 P 求其最长上升子序列长度 Input 第一行一个正整数n,表示序列中整数个数: 第二行是空格隔开的n个整数组成的序列 ...
随机推荐
- Sass--传多个参数
Sass 混合宏除了能传一个参数之外,还可以传多个参数,如: @mixin center($width, $height) { width: $width; height: $height; posi ...
- TCP协议之三次握手四次挥手
一.TCP协议简述 TCP协议位于传输层用来建立传输数据的通道以及传输数据,那么在这一层的tcp协议就涉及到客户端与服务端通信的连接,数据的传输.关闭连接. 通信的连接使用的就是客户端与服务端的三次握 ...
- [BZOJ1023][SHOI2008]cactus仙人掌图 DP
题目链接 套路就是先考虑一般的树上做法.求直径的dp的做法大家应该都会吧. 那么设\(dp[i]\)表示\(i\)的子树中的点到\(i\)的最大距离. 在dp的过程中 \[ ans=\max\{dp[ ...
- 26.LockSupport线程阻塞工具
import java.util.concurrent.locks.LockSupport; /** * 线程阻塞工具类:LockSupport * 可以在线程内任意位置让线程阻塞 */ public ...
- Linux 多个cpp文件的编译(Makefile)
打包so文件: CC = g++ CFLAGS=-Wall -O2 -fPIC TARGET = libbg.so SRCS := $(wildcard *.cpp) OBJS := $(patsub ...
- js 事件对象event
在触发DOM上的某个事件时,会产生一个事件对象event.这个对象中包含着所有与事件有关的信息.包括导致事件的元素,事件的类型以及其他与特定事件相关的信息. <body> <a hr ...
- yii2.0增删改查实例讲解
yii2.0增删改查实例讲解一.创建数据库文件. 创建表 CREATE TABLE `resource` ( `id` int(10) NOT NULL AUTO_INCREMENT, `textur ...
- 【VisualStdio】在VS2015中显示上下文菜单中“创建单元测试”菜单
---恢复内容开始--- VS2012以后创建单元测试的选项被默认隐藏了,创建单元测试变得无比低效率.看msdn的说法好像是想推荐使用Intell Test来替代单元测试的用途,但是还没摸清楚也不敢瞎 ...
- mongo 慢查询配置
我是分片部署,所以慢查询相关的配置是在启动片服务上. 执行查询命令,是在share的primary 上. 1. mongodb慢查询 配置 慢查询数据主要存储在 local库的system.pro ...
- shell 中 比较 diff
diff 可以用来比较文件和文件夹是否相同 比较文件 diff file1 file2 >/dev/null 比较文件夹 diff -rNaq dir1 dir2 >/dev/null - ...