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. 用 requests 模块从 Web 下载文件

    用 requests 模块从 Web 下载文件 requests 模块让你很容易从 Web 下载文件,不必担心一些复杂的问题,诸如网络错误.连接问题和数据压缩.requests 模块不是 Python ...

  2. 【深度学习的实用层面】(一)训练,验证,测试集(Train/Dev/Test sets)

    在配置训练.验证.和测试数据集的过程中做出正确的决策会更好地创建高效的神经网络,所以需要对这三个名词有一个清晰的认识. 训练集:用来训练模型 验证集:用于调整模型的超参数,验证不同算法,检验哪种算法更 ...

  3. nice和renice命令详解

    基础命令学习目录首页 进程调度是linux中非常重要的概念.linux内核有一套高效复杂的调度机制,能使效率极大化,但有时为了实现特定的要求,需要一定的人工干预.比如,你希望操作系统能分配更多的CPU ...

  4. jenkins展示report测试报告的配置

    HTML报告展示 1. 需要HTML Publisher plugin插件 2. 在workspace下的工程(构建)中的目录中存储测试报告 在Jenkins中新建一个job,进入配置项. 首先通过p ...

  5. Scrum立会报告+燃尽图(十二月五日总第三十六次):Final阶段分配任务

    此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2284 项目地址:https://git.coding.net/zhang ...

  6. 20135234mqy-——信息安全系统设计基础第七周学习总结

    第六章 存储器层次结构 存储器系统是一个具有不同容量,成本和访问时间的存储设备的层次结构. CPU寄存器保存着最常用的数据. 靠近CPU的小的,快速的高速缓存存储器作为一部分存储在相对较慢的主存储器( ...

  7. C#窗体随机四则运算 (第四次作业)

    ---恢复内容开始--- 增量内容:1)处理用户的错误输入,比如输入字母或符号等,处理除法运算中分母为0的情况,处理结果为负数的情况,保证是小学水平不出现负数,比如不能出现5-8=-3这种情况:2)用 ...

  8. shiro+springmvc 都使用缓存

    基于涛哥shiro案例16 的这时候要配置service方法的缓存 在spring-config.xml添加 <context:annotation-config /> <cache ...

  9. 团队GIT实战总结

    项目要求 组长博客 遇到的困难及解决办法 组员1(组长):王彬 遇到的困难  在团队任务分工的时候没有充分照顾到所有人,导致队员们的工作量不均. 现场编程时间不够 解决办法 在此对组员们表示抱歉,由于 ...

  10. Daily target小队介绍(刘畅,陈杰,杨有存,唐祎琳,王晓哲,邵汝佳)

    一.团队介绍 1.团队构成: 2.队名: Daily target,我们的口号是Target your day! 3.团队项目描述: 我们计划写一个用于老师发布任务,学生接受任务的安卓app.教师安排 ...