51nod 1376 最长上升子序列的数量

题解

我们设lis[i]为以位置i结尾的最长上升子序列长度,dp[i]为以位置i结尾的最长上升子序列数量。

显然,dp[i]要从前面的一些位置(设为位置j)的dp转移过来。

j要满足下面的条件:

  1. j < i
  2. a[j] < a[i]
  3. lis[j] = lis[i] - 1

dp[i]即为所有满足上述条件的dp[j]之和。

如果我们正常从左到右处理序列,第一条显然可以直接满足(因为大于i的位置还都没处理过)。

为了满足第三条,我们可以把lis相同的值放在一起,用vector存起来。我们设lst[i]为所有满足”lis[j] = i“的j所对应的a[j]构成的vector。

这堆vector有个性质——它是单调不减的。

为什么呢?因为这个vector中,前面的元素是序列中先出现的,后面的是序列中后出现的,而它们同在一个vector中,满足lis相同。假如后面的元素反而比前面的大,那么显然以后面的元素结尾的最长上升子序列可以从前面的元素转移过来,后面元素的lis必然比前面的要大,与它们lis相同矛盾。

那么为了满足上面的第二条条件,我们直接在vector中二分,即可求得所有满足条件的a[j]的和了,也就求出了dp[i]。

总复杂度是\(O(n \log n)\)的。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define INF 0x3f3f3f3f
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef long long ll;
template <class T>
bool read(T &x){
char c;
bool op = 0;
while(c = getchar(), c < '0' || c > '9')
if(c == '-') op = 1;
else if(c == EOF) return 0;
x = c - '0';
while(c = getchar(), c >= '0' && c <= '9')
x = x * 10 + c - '0';
if(op) x = -x;
return 1;
}
template <class T>
void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar('0' + x % 10);
} const int N = 50005, P = 1e9+7;
int n, m, a[N], s[N], cnt;
ll dp[N];
vector <ll> lst[N], sum[N];
//lst[i]存储所有以它结尾LIS长度为i的位置上的数值,
//sum存储对应lst数组中dp值的前缀和。
ll find(int p, ll x){
//这个函数通过在lst[p]中找到最后一个>=x的数,
//从而得到所有“比x小且以它结尾的LIS长度为以x结尾的LIS长度-1”的数的dp值之和。
int l = 0, r = lst[p].end() - lst[p].begin() - 1, mid;
while(l < r){
mid = (l + r + 1) >> 1;
if(lst[p][mid] < x) r = mid - 1;
else l = mid;
}
return ((sum[p].back() - sum[p][l]) % P + P) % P;
}
int main(){
read(n);
for(int i = 1; i <= n; i++)
read(a[i]);
for(int i = 0; i <= n; i++)
lst[i].push_back(INF), sum[i].push_back(0);
lst[0].push_back(-INF), sum[0].push_back(1);
for(int i = 1; i <= n; i++){
int lis; //以位置i结尾的LIS长度
if(!cnt || a[i] > s[cnt]) s[++cnt] = a[i], lis = cnt;
else {
int pos = lower_bound(s + 1, s + cnt + 1, a[i]) - s;
s[pos] = a[i];
lis = pos;
}
lst[lis].push_back(a[i]);
sum[lis].push_back((sum[lis].back() + find(lis - 1, a[i])) % P);
}
write(sum[cnt].back()), enter;
return 0;
}

51nod 1376 最长上升子序列的数量 | DP | vector怒刷存在感!的更多相关文章

  1. 51Nod 1376 最长递增子序列的数量 (DP+BIT)

    题意:略. 析:dp[i] 表示以第 i 个数结尾的LIS的长度和数量,状态方程很好转移,先说长度 dp[i] = max { dp[j] + 1 | a[i] > a[j] && ...

  2. 51nod 1376 最长递增子序列的数量(线段树)

    51nod 1376 最长递增子序列的数量 数组A包含N个整数(可能包含相同的值).设S为A的子序列且S中的元素是递增的,则S为A的递增子序列.如果S的长度是所有递增子序列中最长的,则称S为A的最长递 ...

  3. 51Nod 1376 最长递增子序列的数量 —— LIS、线段树

    题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1376 1376 最长递增子序列的数量 基准时间限制:1 秒 空 ...

  4. 51NOD 1376 最长递增子序列的数量 [CDQ分治]

    1376 最长递增子序列的数量 首先可以用线段树优化$DP$做,转移时取$0...a[i]$的最大$f$值 但我要练习$CDQ$ $LIS$是二维偏序问题,偏序关系是$i<j,\ a_i< ...

  5. 51nod 1376 最长递增子序列的数量(不是dp哦,线段树 +  思维)

    题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1376 题解:显然这题暴力的方法很容易想到就是以每个数为结尾最 ...

  6. 【51nod】1376 最长递增子序列的数量

    数组A包含N个整数(可能包含相同的值).设S为A的子序列且S中的元素是递增的,则S为A的递增子序列.如果S的长度是所有递增子序列中最长的,则称S为A的最长递增子序列(LIS).A的LIS可能有很多个. ...

  7. 51NOD 1006 最长公共子序列 Lcs 动态规划 DP 模板题 板子

    给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). 比如两个串为: abcicba abdkscab ab是两个串的子序列,abc也是,abca也是,其中abca是这两个字符串最 ...

  8. 51nod 1218 最长递增子序列 V2(dp + 思维)

    题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1218 题解:先要确定这些点是不是属于最长递增序列然后再确定这 ...

  9. 51nod1376 最长递增子序列的数量

    O(n2)显然超时.网上找的题解都是用奇怪的姿势写看不懂TAT.然后自己YY.要求a[i]之前最大的是多少且最大的有多少个.那么线段树维护两个值,一个是当前区间的最大值一个是当前区间最大值的数量那么我 ...

随机推荐

  1. Linux命令的那些事(二)

    回顾Linux(一) 学习了以下命令: mkdir/rmdir/ls/rm/pwd/cd/touch/tree/man/--help 想具体了解请看上一篇文章跳转 在Linux中推荐大家使用subli ...

  2. DevOps on AWS之Cloudformation实践篇

    cloudformation入门实践 AWS cloudformation通过模板对AWS云资源进行编排和调用.并且可以通过模板代码层面的修改就可以对现有环境进行升级改造,云端业务的灵活便捷特点展现无 ...

  3. 互评Beta版本——可以低头,但没必要——取件帮

    基于NABCD评论作品,及改进建议 1. 根据(不限于)NABCD评论作品的选题 (1)N(Need,需求) 取件帮是一款有偿互助取件的微信小程序,很大程度上解决了学生因为距离.时间等原因无法取快递的 ...

  4. TeamWork#3,Week5,Scrum Meeting 11.9

    由于经验不足和储备知识不够,最近我们的项目遇到了一些技术问题,需要对项目进行重新计划.我们总结了经验教训,找出了问题所在,明确了要补充的知识,加紧学习,将会在一周之内解决相关问题. 成员 已完成 待完 ...

  5. 【Alpha】阶段第六次Scrum Meeting

    [Alpha]阶段第六次Scrum Meeting 工作情况 团队成员 今日已完成任务 明日待完成任务 刘峻辰 增加教师接口 增加上课信息接口 赵智源 构建后端测试点测试框架 构建后测试点测试框架 肖 ...

  6. datetime类型转换

    Select CONVERT(varchar(100), GETDATE(), 8):14:53:14 Select CONVERT(varchar(100), GETDATE(), 9): 06  ...

  7. C#代码分析(第三周)

    阅读下面程序,请回答如下问题: 问题1:这个程序要找的是符合什么条件的数? 问题2:这样的数存在么?符合这一条件的最小的数是什么? 问题3:在电脑上运行这一程序,你估计多长时间才能输出第一个结果?时间 ...

  8. 实现二叉树(search)

    ★实验任务 可怜的 Bibi 刚刚回到家,就发现自己的手机丢了,现在他决定回头去搜索 自己的手机. 现在我们假设 Bibi 的家位于一棵二叉树的根部.在 Bibi 的心中,每个节点 都有一个权值 x, ...

  9. 深入理解Java虚拟机&运行时数据区

      其中,程序计数器.虚拟机栈.本地方法栈3个区域随线程而生,随线程而灭.

  10. 微信小程序使用函数的三种方法

    使用来自不同页面的函数 函数写在util.js页面 function formatTime(date) { var year = date.getFullYear() var month = date ...