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

题目描述

给定一个序列,初始为空。现在我们将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 100010
using 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;
}

 

posted @ 2017-05-15 15:14  GXZlegend  阅读(408)  评论(0编辑  收藏  举报