[Tjoi2013] 最长上升子序列题解
前言
今天复习平衡树 , 写到的题 , 有感而发 ,就写下此篇题解~~~
原题链接
大体题意就是, 给你一个\(n\) , 从\(1\)插到\(n\) , 每次给你一个插入位置 , 插入\(i\) , 问你此时最长上升子序列;
首先我们不难发现 , 要插入\(i\)到\(pos_{i}\)和\(pos_{i + 1}\)之间 ,首先考虑\([1 \thicksim pos_{i}]\) , 这一段的最长上升子序列不会发生变化 , 而你又发现插入的数都比\([pos_i + 1 \thicksim n]\)区间内的数大 所以这个区间内的的最长上升子序列也不会被影响 , 那么你算插入点的最长上升子序列就是\([1 \thicksim pos_{i}]\)的最长上升子序列加\(1\);说到这,又有一个问题 , 我们插入是使用什么呢????有请高级\(ds\) —— 平衡树登场 , 这个玩意就可以维护 , 只是平衡树的板子需要改一下 , 只需将\(split\)操作权值比较换为下标比较(就是用子树大小 , 因为我们插入也是按照下标插入), 可能光口胡有些不清楚 , 来张图

那好了这个题就结束了;
时间复杂度\(O(n \log n)\)
code
#include<bits/stdc++.h>
using namespace std;
#define lson tree[rt].ls
#define rson tree[rt].rs
mt19937 rd(114514);
const int N = 2e5 + 10;
int n , tot , root;
struct st{
int ls , rs , dp , maxn , rnd , siz;
};
st tree[N << 2];
int New(){
tot ++;
tree[tot] = {0 , 0 , 1 , 1 , rand() , 1};
return tot;
}
void pushup(int rt){
tree[rt].siz = tree[lson].siz + tree[rson].siz + 1;
tree[rt].maxn = max({tree[lson].maxn , tree[rson].maxn , tree[rt].dp});
}
void split(int rt , int v , int &x , int &y){
if(! rt) return x = y = 0 , void();
int tmp = tree[lson].siz + 1;
if(tmp <= v) x = rt , split(rson , v - tmp , rson , y);
else y = rt , split(lson , v , x , lson);
pushup(rt);
}
int merge(int rt , int v){
if(! rt || ! v) return rt | v;
if(tree[rt].rnd < tree[v].rnd){
rson = merge(rson , v);
pushup(rt);
return rt;
}
else{
tree[v].ls = merge(rt , tree[v].ls);
pushup(v);
return v;
}
}
void insert(int v){
int x , y;
split(root , v , x , y);
int nw = New();
tree[nw].dp = tree[nw].maxn = tree[x].maxn + 1;
root = merge(merge(x , nw) , y);
}
int main(){
ios::sync_with_stdio(false); cin.tie(0) ; cout.tie(0);
int p;
cin >> n;
for(int i = 1 ; i <= n ; i ++){
cin >> p , insert(p - 1);
cout << tree[root].maxn << endl;
}
return 0;
}

浙公网安备 33010602011771号