【题意】给定ai,将1~n从小到大插入到第ai个数字之后,求每次插入后的LIS长度。

【算法】树状数组||平衡树

【题解】

这是树状数组的一个用法:O(n log n)寻找前缀和为k的最小位置。(当数列中只有0和1时,转化为求对应排名的数字,就是简单代替平衡树)

根据树状数组的二进制分组规律,从大到小进行倍增,可以发现每次需要加的Σa[i],i∈(now,now+(1<<i)]刚好就是c[now+(1<<i)]。

文字表述就是,跳跃到的位置的c[]刚好表示中间跳跃的数字和,这是树状数组二进制分组规律的特殊性质。

还需要注意的是,实际上需要寻找前缀和<k的最大位置,最后+1。(否则会被目标数字后面的0干扰)

利用上述的方法,初始树状数组全部置为1,然后从n到1倒着寻找并删除,就可以得到每个数字在最终序列中的位置。

这道题由于从小到大插入,可以发现将所有数字全部插入也不会破坏过程中需要的LIS(只会在最后增长)。

那么第i个答案就是以数字1~i结尾的LIS的最长长度。

所以令f[i]表示最终序列中以数字 i 结尾的LIS,则第i个答案就是min(f[j]),j=1~i。(是数字i,不是第i个位置)

求解f[i]只需在O(n log n)求解整个最终序列的LIS的过程中求出即可。

总复杂度O(n log n)。

最后,代码中运用的线性构造树状数组,原理十分简单。

首先要求1~n都有数字(0也行),然后每个数加到自身c[i]+=a[i],再贡献一下父亲c[i+lowbit(i)]+=c[i]就可以了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define lowbit(x) (x&-x)
using namespace std;
const int maxn=;
int a[maxn],b[maxn],c[maxn],g[maxn],anss[maxn],n;
int read(){
char c;int s=,t=;
while(!isdigit(c=getchar()))if(c=='-')t=-;
do{s=s*+c-'';}while(isdigit(c=getchar()));
return s*t;
}
void insert(int x,int k){for(int i=x;i<=n;i+=lowbit(i))c[i]+=k;}
int find(int x){
int now=,ans=;
for(int i=;i>=;i--){
now+=(<<i);
if(now<n&&ans+c[now]<x)ans+=c[now];//< near
else now-=(<<i);
}
now++;
insert(now,-);
return now;
}
int max(int a,int b){return a<b?b:a;}
int main(){
n=read();
for(int i=;i<=n;i++){
a[i]=read();
c[i]++;c[i+lowbit(i)]+=c[i];
}
for(int i=n;i>=;i--)b[find(a[i]+)]=i;
int m=;
for(int i=;i<=n;i++){
int s=lower_bound(g+,g+m+,b[i])-g;
if(s>m)g[++m]=b[i];else g[s]=b[i];
anss[b[i]]=s;
}
for(int i=;i<=n;i++){
anss[i]=max(anss[i-],anss[i]);
printf("%d\n",anss[i]);
}
return ;
}

补充平衡树写法(fhq-treap)。

每个点记录以这个点结尾的LIS,然后插入平衡树中,平衡树维护区间max值。

怎么得到以每个点结尾的LIS?因为当前加入的点不可能改变之前的点的LIS,所以只需要区间查询该点插入位置之前的max+1就是以这个点结尾的LIS。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
const int maxn=;
struct cyc{int l,r,rnd,num,mx,sz;}t[maxn];
int root,n;
int read(){
char c;int s=,t=;
while(!isdigit(c=getchar()))if(c=='-')t=-;
do{s=s*+c-'';}while(isdigit(c=getchar()));
return s*t;
}
void up(int k){
t[k].sz=t[t[k].l].sz+t[t[k].r].sz+;
t[k].mx=max(t[k].num,max(t[t[k].l].mx,t[t[k].r].mx));
}
void split(int k,int &l,int &r,int x){
if(!k)return void(l=r=);
if(x<t[t[k].l].sz+){
r=k;
split(t[k].l,l,t[k].l,x);
}
else{
l=k;
split(t[k].r,t[k].r,r,x-t[t[k].l].sz-);
}
up(k);
}
int merge(int a,int b){
if(!a||!b)return a^b;
if(t[a].rnd<t[b].rnd){
t[a].r=merge(t[a].r,b);
up(a);
return a;
}
else{
t[b].l=merge(a,t[b].l);
up(b);
return b;
}
}
void insert(int k,int x){
int a,b;
split(root,a,b,x);
t[k]=(cyc){,,rand(),t[a].mx+,t[a].mx+,};
root=merge(a,k);
root=merge(root,b);
printf("%d\n",t[root].mx);
}
int main(){
n=read();root=;
for(int i=;i<=n;i++)insert(i,read());
return ;
}

【BZOJ】3173: [Tjoi2013]最长上升子序列(树状数组)的更多相关文章

  1. bzoj3173: [Tjoi2013]最长上升子序列(树状数组+二分倒推)

    3173: [Tjoi2013]最长上升子序列 题目:传送门 题解:  好题! 怎么说吧...是应该扇死自己...看错了两次题: 每次加一个数的时候,如果当前位置有数了,是要加到那个数的前面,而不是直 ...

  2. Bzoj 3173: [Tjoi2013]最长上升子序列 平衡树,Treap,二分,树的序遍历

    3173: [Tjoi2013]最长上升子序列 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1183  Solved: 610[Submit][St ...

  3. BZOJ 3173: [Tjoi2013]最长上升子序列

    3173: [Tjoi2013]最长上升子序列 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1524  Solved: 797[Submit][St ...

  4. BZOJ 3173: [Tjoi2013]最长上升子序列( BST + LIS )

    因为是从1~n插入的, 慢插入的对之前的没有影响, 所以我们可以用平衡树维护, 弄出最后的序列然后跑LIS就OK了 O(nlogn) --------------------------------- ...

  5. BZOJ 3173: [Tjoi2013]最长上升子序列 [splay DP]

    3173: [Tjoi2013]最长上升子序列 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1613  Solved: 839[Submit][St ...

  6. BZOJ 3173 [Tjoi2013] 最长上升子序列 解题报告

    这个题感觉比较简单,但却比较容易想残.. 我不会用树状数组求这个原排列,于是我只好用线段树...毕竟 Gromah 果弱马. 我们可以直接依次求出原排列的元素,每次找到最小并且最靠右的那个元素,假设这 ...

  7. bzoj 3173 [Tjoi2013]最长上升子序列 (treap模拟+lis)

    [Tjoi2013]最长上升子序列 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2213  Solved: 1119[Submit][Status] ...

  8. BZOJ 3173: [Tjoi2013]最长上升子序列 (线段树+BIT)

    先用线段树预处理出每个数最终的位置.然后用BIT维护最长上升子序列就行了. 用线段树O(nlogn)O(nlogn)O(nlogn)预处理就直接倒着做,每次删去对应位置的数.具体看代码 CODE #i ...

  9. BZOJ 3173 最长上升子序列(树状数组+二分+线段树)

    给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.每插入一个数字,我们都想知道此时最长上升子序列长度是多少? 由于序列是顺序插入的,所以当前插入的数字对之 ...

  10. hdu 5773 The All-purpose Zero 最长上升子序列+树状数组

    题目链接:hdu 5773 The All-purpose Zero 官方题解:0可以转化成任意整数,包括负数,显然求LIS时尽量把0都放进去必定是正确的. 因此我们可以把0拿出来,对剩下的做O(nl ...

随机推荐

  1. Java单例模式&static成员变量 区别

    当需要共享的变量很多时,使用static变量占用内存的时间过长,在类的整个生命周期. 而对象只是存在于对象的整个生命周期.   //饿汉式 class Single//类一加载,对象就已经存在了. { ...

  2. 七周七语言之使用prolog解决爱因斯坦斑马难题

    如果你想获得更好的阅读体验,可以前往我在 github 上的博客进行阅读,http://lcomplete.github.io/blog/2013/06/28/sevenlang-prolog/. 目 ...

  3. 【beta】阶段会议记录汇总

    第一次: http://www.cnblogs.com/yumiaomiao/p/6026752.html 第二次: http://www.cnblogs.com/liquan/p/6031802.h ...

  4. 使用python操作Memcache、Redis、RabbitMQ、

    Memcache 简述: Memcache是一套分布式的高速缓存系统,由LiveJournal的Brad Fitzpatrick开发,但目前被许多网站使用以提升网站的访问速度,尤其对于一些大型的.需要 ...

  5. Web前端面试宝典(最新)

    第一部分:HTML问答题   1.简述一下你对HTML语义化的理解? 用正确的标签做正确的事情. html语义化让页面的内容结构化,结构更清晰,便于对浏览器.搜索引擎解析;即使在没有样式CSS情况下也 ...

  6. 第79天:jQuery事件总结(二)

    上一篇讲到jQuery中的事件,深入学习了加载DOM和事件绑定的相关知识,这篇主要深入讨论jQuery事件中的合成事件.事件冒泡和事件移除等内容. 一.合成事件 jQuery有两个合成事件——hove ...

  7. AtCoder Regular Contest 074 瞎打记

    (很长时间没更新了>_<) 由于机房的网总是奥妙重重,开考30多分钟之后我才登进去... 然后发现T1是个简单枚举,1A.T2是个简单优先队列,1A.T3似乎需要一点推导,先看了T4发现是 ...

  8. Django 2.0 学习(14):Django ORM 数据库操作(上)

    Django ORM 数据库操作(上) ORM介绍 映射关系: 数据库表名 ---------->类名:数据库字段 ---------->类属性:数据库表一行数据 ----------&g ...

  9. 【BZOJ1494】【NOI2007】生成树计数(动态规划,矩阵快速幂)

    [BZOJ1494][NOI2007]生成树计数(动态规划,矩阵快速幂) 题面 Description 最近,小栋在无向连通图的生成树个数计算方面有了惊人的进展,他发现: ·n个结点的环的生成树个数为 ...

  10. CF891E [数学题]

    1.答案=初始乘积-最终乘积的期望.然后直接dp+ntt是O(nklogk) 2.考虑展开式子ans=sum(a[i]-b[i]),大概感受一下未知数个数相同的项系数相同,问题在于如何求系数 3.没思 ...