51nod 1376 最长上升子序列的数量 | DP | vector怒刷存在感!
51nod 1376 最长上升子序列的数量
题解
我们设lis[i]为以位置i结尾的最长上升子序列长度,dp[i]为以位置i结尾的最长上升子序列数量。
显然,dp[i]要从前面的一些位置(设为位置j)的dp转移过来。
j要满足下面的条件:
- j < i
- a[j] < a[i]
- 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怒刷存在感!的更多相关文章
- 51Nod 1376 最长递增子序列的数量 (DP+BIT)
		题意:略. 析:dp[i] 表示以第 i 个数结尾的LIS的长度和数量,状态方程很好转移,先说长度 dp[i] = max { dp[j] + 1 | a[i] > a[j] && ... 
- 51nod 1376 最长递增子序列的数量(线段树)
		51nod 1376 最长递增子序列的数量 数组A包含N个整数(可能包含相同的值).设S为A的子序列且S中的元素是递增的,则S为A的递增子序列.如果S的长度是所有递增子序列中最长的,则称S为A的最长递 ... 
- 51Nod 1376 最长递增子序列的数量 —— LIS、线段树
		题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1376 1376 最长递增子序列的数量 基准时间限制:1 秒 空 ... 
- 51NOD 1376 最长递增子序列的数量 [CDQ分治]
		1376 最长递增子序列的数量 首先可以用线段树优化$DP$做,转移时取$0...a[i]$的最大$f$值 但我要练习$CDQ$ $LIS$是二维偏序问题,偏序关系是$i<j,\ a_i< ... 
- 51nod 1376 最长递增子序列的数量(不是dp哦,线段树 +  思维)
		题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1376 题解:显然这题暴力的方法很容易想到就是以每个数为结尾最 ... 
- 【51nod】1376 最长递增子序列的数量
		数组A包含N个整数(可能包含相同的值).设S为A的子序列且S中的元素是递增的,则S为A的递增子序列.如果S的长度是所有递增子序列中最长的,则称S为A的最长递增子序列(LIS).A的LIS可能有很多个. ... 
- 51NOD 1006 最长公共子序列 Lcs 动态规划 DP 模板题 板子
		给出两个字符串A B,求A与B的最长公共子序列(子序列不要求是连续的). 比如两个串为: abcicba abdkscab ab是两个串的子序列,abc也是,abca也是,其中abca是这两个字符串最 ... 
- 51nod 1218 最长递增子序列 V2(dp + 思维)
		题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1218 题解:先要确定这些点是不是属于最长递增序列然后再确定这 ... 
- 51nod1376 最长递增子序列的数量
		O(n2)显然超时.网上找的题解都是用奇怪的姿势写看不懂TAT.然后自己YY.要求a[i]之前最大的是多少且最大的有多少个.那么线段树维护两个值,一个是当前区间的最大值一个是当前区间最大值的数量那么我 ... 
随机推荐
- Vue02
			3.Vue对象提供的属性功能 过滤器,就是vue允许开发者自定义的文本格式化函数,可以使用在两个地方:输出内容和操作数据中. 定义过滤器的方式有两种. 1 使用Vue.filter()进行全局定义 V ... 
- 对于新手来说,Python 中有哪些难以理解的概念?
			老手都是从新手一路过来的,提起Python中难以理解的概念,可能很多人对于Python变量赋值的机制有些疑惑,不过对于习惯于求根究底的程序员,只有深入理解了某个事物本质,掌握了它的客观规律,才能得心应 ... 
- [leetcode-921-Minimum Add to Make Parentheses Valid]
			Given a string S of '(' and ')' parentheses, we add the minimum number of parentheses ( '(' or ')', ... 
- 探路者-Beta发布中间产物
			版本控制 版本控制报告:http://www.cnblogs.com/linym762/p/7881047.html git地址:https://git.coding.net/clairewyd/to ... 
- 课堂实践ASL博客
			实践博客 二分法查找元素 1.首先定义三个位置min,mid,max 2.每次从所有元素所处位置的中间开始查找(所有元素必须以由小及大顺序排列完毕) 3.当中间元素大于所查找元素时,从中间元素(mid ... 
- 软工大作业DB天气项目风险评估
			风险 发生概率 损失 风险度 解决方案 项目延期 80% 浪费时间,项目完成进度降低,考试得分低 79% 提前做好详细的准备工作,各方面做好沟通. 工作效率低下 30% 影响进度,使项目延期. 85% ... 
- Java基础第一节.Java简介
			第一节 Java简介 Java是一个由Sun公司开发而成的新一代的编程语言. Java语言是对软件开发有深远影响.应用前景广泛.具有丰富的类库.继承了C++的传统(摈弃了某些不足)广泛使用的网络编程语 ... 
- 软工 · 第十二次作业 - Beta答辩总结
			福大软工 · 第十二次作业 - Beta答辩总结 写第十二次的时候操作失误直接在Beta版本的博客里改了...第七次冲刺的作业链接补在这里 Beta(7/7) 组长本次博客作业链接 项目宣传视频链接 ... 
- 第二次作业<1>
			1001.A+B Format (20) ac代码 1. 解题思路 先求和,再输出. 答案区间为-2,000,000至2,000,000,将答案分为三个区段分类讨论.虽然觉得很烦但是想不出更好的方法. ... 
- Matlab图像匹配问题
			已知一个任意形状,查找在大图像中最接近的形状位置. 输入:一个小图形状和一张大图 输出:最接近的形状在大图中的位置 假设: (1)已知形状与目标形状有一定的形变. (2)形状与大图像均为二值图像,图中 ... 
