【bzoj3173】[Tjoi2013]最长上升子序列 Treap

来源:互联网 发布:直销软件开发价格 编辑:程序博客网 时间:2024/06/03 21:31

原文链接:http://www.cnblogs.com/GXZlegend/p/6856502.html

作者:GXZlegend

题目描述

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

输入

第一行一个整数N,表示我们要将1到N插入序列中,接下是N个数字,第k个数字Xk,表示我们将k插入到位置Xk(0<=Xk<=k-1,1<=k<=N)

输出

N行,第i行表示i插入Xi位置后序列的最长上升子序列的长度是多少。

样例输入

3
0 0 2

样例输出

1
1
2


题解

Treap

考虑到数据是从小到大插入的,所以每次插入后这个数只会对自身有影响,并不会对前后有影响;而且自身的答案只受其前边位置的答案的影响。

具体地说,设f[i]表示最后一个数为i的最长上升子序列长度,那么插入时f[i]=max{f[j]}+1(pos(j)<pos(i))。

查询时查询的是整个f数组的最大值。

这样就需要一个数据结构,支持插入一个数、查询以1开头的区间的最大值,可以使用Treap搞定。

这里的insert函数与普通的不同,是指定位置的插入,所以判断时比较的是子树大小。

#include <cstdio>#include <cstdlib>#include <algorithm>#define N 100010using namespace std;int f[N] , l[N] , r[N] , si[N] , rnd[N] , maxn[N] , root , tot;void pushup(int k){    si[k] = si[l[k]] + si[r[k]] + 1 , maxn[k] = max(f[k] , max(maxn[l[k]] , maxn[r[k]]));}void zig(int &k){    int t = l[k];    l[k] = r[t] , r[t] = k , si[t] = si[k] , pushup(k) , k = t;}void zag(int &k){    int t = r[k];    r[k] = l[t] , l[t] = k , si[t] = si[k] , pushup(k) , k = t;}void ins(int &k , int x , int w){    if(!k) k = ++tot , f[k] = w , rnd[k] = rand();    else if(x <= si[l[k]])    {        ins(l[k] , x , w);        if(rnd[l[k]] < rnd[k]) zig(k);    }    else    {        ins(r[k] , x - si[l[k]] - 1 , w);        if(rnd[r[k]] < rnd[k]) zag(k);    }    pushup(k);}int query(int k , int x){    if(!k) return 0;    if(x <= si[l[k]]) return query(l[k] , x);    return max(max(maxn[l[k]] , f[k]) , query(r[k] , x - si[l[k]] - 1));}int main(){    int n , x;    scanf("%d" , &n);    while(n -- ) scanf("%d" , &x) , ins(root , x , query(root , x) + 1) , printf("%d\n" , maxn[root]);    return 0;}

阅读全文
0 0
原创粉丝点击