【bzoj3173-最长上升子序列-一题两解】
这道题不就是简单的DP吗,BZOJ在水我!不,你是错的。
·本题特点:
不断向不同位置插入数字(按数字1,2,3,4,5,6……),需要求出每一次插入后的最长上升子序列。
·分析
首先我们要着眼于“数字从小到大不断插入”这个行为的特点。如图:
我们设f[i]表示当前插入的数是i的时候(这道题特殊,i同样表示插入了i个数),那么我们发现,如果能够快速确定最终序列长什么样子,那么就可以按照读入(插入)顺序进行一次平凡的DP。
·一切都精确地指向这个序列最终的样子是什么这一问题。也就是说呀:我们需要一种高超的技术来维护区间插入操作。然后有人将两种方法分别称为“无脑”和“奇妙”。
[1]“无脑”数据结构——Treap
做了这题不经意间Treap就可以入门。Treap的特点是维护权值和优先级两个因素,这样的目的同时满足BST(可爱排序二叉树)和HEAP(堆)的性质,这样使得它可以维护序列的某种关键字下的排序(BST)和树的平衡(HEAP)。插入操作是Treap的基本玩法,但是插入要设法满足BST和HEAP的性质,也就是维护平衡和保证有序。维护平衡就是防止树的不当插入导致树退化成低级结构(最极端的是一条链,此时任何操作都是O(n)而不是O(logn)),维护平衡的方法就是旋转。旋转的特点是改变某些节点的父子关系以保证堆的性质,同时不会破坏维护的序列的排序(排序实际上就是树的中序遍历啦),对于一般的Treap,除了上述特点外,还会附带一个Maintain(大米饼常用Keep)函数来在旋转后维护节点信息(比如u的子树儿子节点个数等)。上述内容为Treap入门,然后使劲看代码搞懂Treap问题不大。
对于此题,我们的美妙Treap的排序权值是当前的插入位置,与其对应地,我们用SIZE[u]标记每一个子树的节点(表示一段区间内已插入的数的个数)。最后一旦我们拿到了最终序列,就自由了,你可以尽情的用任意优化方式进行一个nlongn的DP就可以啦(例如树状数组,zkw线段树,辅助数组二分法等)。
在数组版和指针版挣扎了一会,最终还是走上数组版Treap的道路。
大米饼习惯:ch[u][]表示左右儿子节点。一些基本操作在代码里很清晰。
#include<stdio.h>
#include<algorithm>
#define go(i,a,b) for(int i=a;i<=b;i++)
using namespace std;const int N=;
int n,v,ch[N][],sz,P[N],siz[N],a[N],f[N],c[N],t,Re;
void Keep(int u){siz[u]=siz[ch[u][]]+siz[ch[u][]]+;}
void ADD(int x,int d){while(x<=n)c[x]=max(c[x],d),x+=x&-x;}
void Get(int x){Re=;while(x)Re=max(Re,c[x]),x-=x&-x;return;}
void Dfs(int u){if(!u)return;Dfs(ch[u][]);a[++t]=u;Dfs(ch[u][]);}
void Rotate(int &u,int d){ch[u][d]=ch[v=ch[u][d]][d^];ch[v][d^]=u;u=v;}
void Insert(int &u,int pos)
{
if(!u){siz[u=++sz]=;P[u]=rand();return;}
int d=siz[ch[u][]]<pos;siz[u]++;
Insert(ch[u][d],d?pos--siz[ch[u][]]:pos);
if(P[u]<P[ch[u][d]])Rotate(u,d),Keep(ch[u][d^]),Keep(u);
}
int main()
{
scanf("%d",&n);int _=;
go(i,,n)scanf("%d",&v),Insert(_,v);Dfs(_);
go(i,,n)Get(a[i]),ADD(a[i],f[a[i]]=Re+);
go(i,,n)f[i]=max(f[i],f[i-]),printf("%d\n",f[i]);
return ;
}//Paul_Guderian
跑出来800ms。然后就发现了接下来的方法,400ms。
[2]倒序构造法:对于构造最终序列,可以使用倒序的方法来反向模拟数的插入过程。可以理解我们现将所有数放进来,然后一个个拔出去。我们用一个数组表示:如果这个数还没有被拔出去,那么数组这一位为1,否则为0。如图:假设现在应该拔出a[i](即输入的:数字i插入到i位置上)
那么它在哪里?我们就在这个数组中查找第一个前缀和等于i的位置,那里就是它的真实位置(注意这里的i可以理解为第i个插入的),所以这一步我们使用二分法加一个维护前缀的东西就可以完成(偷懒就用个STL bound挺好的)。我们又一次的到了最终序列,然后就随意DP+优化就完成了。
在代码中,大米饼出于省空间和统一化等原因,直接用树状数组维护了前缀,自认为很方便呀!
(里面夹杂了一些读入优化输出优化)
#include<stdio.h>
#include<cstring>
#include<algorithm>
#define go(i,a,b) for(int i=a;i<=b;i++)
#define ro(i,a,b) for(int i=a;i>=b;i--)
using namespace std;const int N=;
int a[N],c[N],f[N],n,l,r,M,t;
inline void Add(int x,int d){while(x<=n)c[x]+=d,x+=x&-x;}
inline void Up(int x,int d){while(x<=n)c[x]=max(c[x],d),x+=x&-x;}
inline int Sum(int x){int res=;while(x)res+=c[x],x-=x&-x;return res;}
inline int Max(int x){int M=;while(x)M=max(M,c[x]),x-=x&-x;return M;}
inline void Nick(int x){if(x<)putchar('-'),x=-x;if(x>)Nick(x/);putchar(x%+'');}
inline int Judy(int &x)
{
x=;int w=;char c=;
while(c<''||c>''){if(c=='-')w=-;c=getchar();}
while(c>=''&&c<='')x=x*+c-'',c=getchar();x*=w;return ;
}
int main()
{
Judy(n);go(i,,n)Judy(a[i]),Add(i,);
ro(i,n,){l=,r=n;while(l<r)M=l+r>>,Sum(M)<a[i]+?l=M+:r=M;Add(a[i]=r,-);}
go(i,,n)Up(a[i],f[i]=Max(a[i])+),f[i]=max(f[i],f[i-]),Nick(f[i]),puts("");
return ;
}//Paul_Guderian
最后一个时间效率比较:
那天我见到他们在聚会上,
他们说那真是段糟糕的日子,
那不是他们想要的生活,
也许幸福是再次寻找在路上……——————汪峰《请把我在路上叫醒》
【bzoj3173-最长上升子序列-一题两解】的更多相关文章
- POJ 3903 Stock Exchange 最长上升子序列入门题
题目链接:http://poj.org/problem?id=3903 最长上升子序列入门题. 算法时间复杂度 O(n*logn) . 代码: #include <iostream> #i ...
- [bzoj3173]最长上升子序列_非旋转Treap
最长上升子序列 bzoj-3173 题目大意:有1-n,n个数,第i次操作是将i加入到原有序列中制定的位置,后查询当前序列中最长上升子序列长度. 注释:1<=n<=10,000,开始序列为 ...
- POJ-1458.CommonSubsequence.(DP:最长公共子序列裸题)
本题大意:给出两个字符串,让你求出最长公共子序列的长度并输出. 本题思路:本题是经典的DP问题,由于是两个字符串,那么我们就用一个二维数组来进行区分,用dp[ i ][ j ]来表示在s1和s2中分别 ...
- 51nod 1218 最长递增子序列 | 思维题
51nod 1218 最长递增子序列 题面 给出一个序列,求哪些元素可能在某条最长上升子序列中,哪些元素一定在所有最长上升子序列中. 题解 YJY大嫂教导我们,如果以一个元素结尾的LIS长度 + 以它 ...
- Longest Increasing Subsequences(最长递增子序列)的两种DP实现
一.本文内容 最长递增子序列的两种动态规划算法实现,O(n^2)及O(nlogn). 二.问题描述 最长递增子序列:给定一个序列,从该序列找出最长的 升序/递增 子序列. 特点:1.子序列不要 ...
- [BZOJ3173]最长上升子序列
Problem 给你n个数A1~An,每次将i插入第Ai位后,最后输出每次插入后这个数列的最长上升子序列 Solution 这道题非常的妙.首先如果新加入的这个数构成了最长上升子序列,由于在它插入之前 ...
- poj 1080 ——Human Gene Functions——————【最长公共子序列变型题】
Human Gene Functions Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 17805 Accepted: ...
- POJ-1458(LCS:最长公共子序列模板题)
Common Subsequence POJ-1458 //最长公共子序列问题 #include<iostream> #include<algorithm> #include& ...
- [LeetCode每日一题]1143. 最长公共子序列
[LeetCode每日一题]1143. 最长公共子序列 问题 给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度.如果不存在 公共子序列 ,返回 0 . 一个字符串 ...
随机推荐
- 同样是IT培训,为什么人家月薪过万,你才几千,问题在哪?!
听过一句话"360行,行行转IT",虽然有些夸张,但也不难看出IT行业的火爆程度.从李总理提的"互联网+大数据"开始,中国的这场"互联网+" ...
- css3动画transition详解2
transition主要包含四个属性值:执行变换的属性:transition-property,变换延续的时间:transition-duration,在延续时间段,变换的速率变化transition ...
- LeetCode & Q122-Best Time to Buy and Sell Stock II-Easy
Description: Say you have an array for which the ith element is the price of a given stock on day i. ...
- salesforce零基础学习(八十七)Apex 中Picklist类型通过Control 字段值获取Dependent List 值
注:本篇解决方案内容实现转自:http://mysalesforceescapade.blogspot.com/2015/03/getting-dependent-picklist-values-fr ...
- 安装CentOS7,连接mysql提示密码错误
1.grep 'temporary password' /var/log/mysqld.log 如果上面命令没有查看到密码 2.修改my.cnf文件.在mysqld下加入skip-grant-tabl ...
- Python内置函数(54)——callable
英文文档: callable(object) Return True if the object argument appears callable, False if not. If this re ...
- Angular 学习笔记 ( CDK - Overlays )
更新 : 2018-01-30 ng 的 overlap 在关闭的时候对 backdrop 做了一个 style pointer 目的是让 backdrop 不被 2 次点击, 但是呢, css p ...
- ELK学习总结(1-2)安装ElasticSearch
1.下载安装 Centos6.4 jdk1.8.20以上 elasticsearch::https://www.elastic.co/downloads/elasticsearch ...
- java的分数类
概述 分数类在算法中非常重要, 而在java中不那么重要,java基础类库提供 了biginteger了,提供类似方式, package 组合数学; public class Fraction { p ...
- leetcode算法: Find the Difference
Given two strings s and t which consist of only lowercase letters.String t is generated by random sh ...