最长上升子序列(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个整数组成的序列 ...
随机推荐
- Vue:替换/合并现有的特性
假设这是 bs-date-input 的模板: <input type="date" class="form-control"> 为了给该日期选择器 ...
- ThreadPoolTaskExecutor使用详解
当我们需要实现并发.异步等操作时,通常都会使用到ThreadPoolTaskExecutor,现对其使用稍作总结. 配置ThreadPoolTaskExecutor通常通过XML方式配置,或者通过Ex ...
- 【抓包工具之Fiddler】增加IP列;session高亮
Fiddler 在处理每个session时,脚本文件CustomRules.js中的方法都会运行,该脚本使得你可以隐藏,标识或任意修改负责的session.规则脚本在运行状态下就可以修改并重新编译,不 ...
- k8s-calico【转载】
环境 系统:centos7.3192.168.40.50 local-master192.168.40.51 local-node1192.168.40.52 local-node2 master: ...
- css----overflow(布局)
CSS overflow 属性用于控制内容溢出元素框时显示的方式. CSS Overflow CSS overflow 属性可以控制内容溢出元素框时在对应的元素区间内添加滚动条. overflow属性 ...
- Redis5离线安装
1. 直接上redis官网安装包, 然后上传服务器 https://redis.io/download 2. 解压 tar -zxvf redis-5.0.6.tar.gz 3. 进入redis根目标 ...
- PHP curl_multi_add_handle函数
curl_multi_add_handle — 向curl批处理会话中添加单独的curl句柄 说明 int curl_multi_add_handle ( resource $mh , resourc ...
- HDU 6034 Balala Power! —— Multi-University Training 1
Talented Mr.Tang has nn strings consisting of only lower case characters. He wants to charge them wi ...
- E: Could not get lock /var/lib/dpkg/lock-frontend - open (11: Resource temporarly unavailable)
1. 问题详细提示如下: E: Could not get lock /var/lib/dpkg/lock-frontend - open (11: Resource temporarly unava ...
- JS继承 实现方式
JS中继承方式的实现有多种方法,下面是比较推荐的方法,其它继承方式可做了解: function object(o) { function F() {} F.prototype = o; return ...