【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; }