这道题不就是简单的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-最长上升子序列-一题两解】的更多相关文章

  1. POJ 3903 Stock Exchange 最长上升子序列入门题

    题目链接:http://poj.org/problem?id=3903 最长上升子序列入门题. 算法时间复杂度 O(n*logn) . 代码: #include <iostream> #i ...

  2. [bzoj3173]最长上升子序列_非旋转Treap

    最长上升子序列 bzoj-3173 题目大意:有1-n,n个数,第i次操作是将i加入到原有序列中制定的位置,后查询当前序列中最长上升子序列长度. 注释:1<=n<=10,000,开始序列为 ...

  3. POJ-1458.CommonSubsequence.(DP:最长公共子序列裸题)

    本题大意:给出两个字符串,让你求出最长公共子序列的长度并输出. 本题思路:本题是经典的DP问题,由于是两个字符串,那么我们就用一个二维数组来进行区分,用dp[ i ][ j ]来表示在s1和s2中分别 ...

  4. 51nod 1218 最长递增子序列 | 思维题

    51nod 1218 最长递增子序列 题面 给出一个序列,求哪些元素可能在某条最长上升子序列中,哪些元素一定在所有最长上升子序列中. 题解 YJY大嫂教导我们,如果以一个元素结尾的LIS长度 + 以它 ...

  5. Longest Increasing Subsequences(最长递增子序列)的两种DP实现

    一.本文内容 最长递增子序列的两种动态规划算法实现,O(n^2)及O(nlogn).     二.问题描述 最长递增子序列:给定一个序列,从该序列找出最长的 升序/递增 子序列. 特点:1.子序列不要 ...

  6. [BZOJ3173]最长上升子序列

    Problem 给你n个数A1~An,每次将i插入第Ai位后,最后输出每次插入后这个数列的最长上升子序列 Solution 这道题非常的妙.首先如果新加入的这个数构成了最长上升子序列,由于在它插入之前 ...

  7. poj 1080 ——Human Gene Functions——————【最长公共子序列变型题】

    Human Gene Functions Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 17805   Accepted:  ...

  8. POJ-1458(LCS:最长公共子序列模板题)

    Common Subsequence POJ-1458 //最长公共子序列问题 #include<iostream> #include<algorithm> #include& ...

  9. [LeetCode每日一题]1143. 最长公共子序列

    [LeetCode每日一题]1143. 最长公共子序列 问题 给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度.如果不存在 公共子序列 ,返回 0 . 一个字符串 ...

随机推荐

  1. Linux下进程间通信--消息队列

    消息队列的定义遍地都是,不想移驾,请看下文: 一.定义: 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法. 每个数据块都被认 为是有一个类型,接收者进程接收的数据块可以有不同的类型值.我 ...

  2. Tornado 用户身份验证框架

    1.安全cookie机制 import tornado.web session_id = 1 class MainHandler(tornado.web.RequestHandler): def ge ...

  3. tomcat下的web.xml和项目中的web.xml

    Tomcat 服务器中存在一个web.xml文件 在项目文件夹中同样存在一个web.xml文件 那这两个文件有什么区别呢? tomcat中的web.xml是通用的,如果不设置,那么就会默认是同tomc ...

  4. HDFS的7个设计特点

    1.Block的放置:默认不配置.一个Block会有三份备份,一份放在NameNode指定的DataNode,另一份放在与指定DataNode非同一Rack上的DataNode,最后一份放在与指定Da ...

  5. Oracle闪回技术

    (一)闪回技术概要 闪回技术是数据库备份与恢复的重要补充手段,主要包括以下7种特性: 特性 原理 数据库支持 闪回查询(Flashback Query) 利用undo表空间中的回退信息,查询过去某个时 ...

  6. Mego开发文档 - 数据属性生成值

    数据属性生成值 该功能用于在数据插入或更新时为指定属性生成期望的值,Mego提供了非常灵活的实现方式以满足各种数据提交时的自动赋值问题. 生成值目的及模式 在Mego中生成值的目的一定是插入数据或更新 ...

  7. java中类的三大特征之多态

    Java 多态 同一种事物由于条件不同,展示出不同的结果,叫做多态. 父类的引用类型,由于使用不同的子类对象实例,而执行不同的操作. 多态存在的三个必要条件 1. 子类继承父类: 2. 子类重写父类方 ...

  8. 阿里云API网关(12)为员工创建子账号,实现分权管理API:使用RAM管理API

    网关指南: https://help.aliyun.com/document_detail/29487.html?spm=5176.doc48835.6.550.23Oqbl 网关控制台: https ...

  9. Tomcat(1-1)重置Tomcat8.5管理员的用户名和密码

    1.访问 http://localhost:8080/,点击 [manager app],提示输入用户名和密码,admin/admin后报错.  2.解决办法:重置Tomcat8.5管理员的用户名和密 ...

  10. webservice面试题

    webservice是什么? 1.基于WEB的服务,服务端整出一些资源让客户端应用访问(提供数据) 2.webservice是一个跨语言跨平台的规范(抽象) 3.是多个跨语言跨平台的应用间通信整合的方 ...