版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/C20181220_xiang_m_y/article/details/88901288
题面:
给定一个序列,初始为空。现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置。每插入一个数字,我们都想知道此时最长上升子序列长度是多少?
Input
第一行一个整数N,表示我们要将1到N插入序列中,接下是N个数字,第k个数字Xk,表示我们将k插入到位置Xk(0<=Xk<=k-1,1<=k<=N)N<=100000
题目分析:
法一:Treap板题
维护size,子树max,当前点的v。
Code:
#include<cstdio>
#include<cctype>
#include<deque>
#include<algorithm>
#define maxn 100005
char cb[1<<15],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
using namespace std;
int Rnd(){
int G=3;
return G=G*3ll%998244353;
}
#define lc ch[x][0]
#define rc ch[x][1]
int n,ans;
int mx[maxn],f[maxn],ch[maxn][2],rnd[maxn],siz[maxn],tot,rt;
inline void upd(int x){
siz[x]=siz[lc]+siz[rc]+1;
mx[x]=max(f[x],max(mx[lc],mx[rc]));
}
void rot(int &x,int c){
int y=ch[x][c];
ch[x][c]=ch[y][!c],ch[y][!c]=x;
upd(x),x=y;
}
void insert(int &x,int p,int v){
if(!x) {siz[x=++tot]=1,mx[x]=f[x]=v,rnd[x]=Rnd();return;}
if(p<=siz[lc]) {insert(lc,p,v);if(rnd[lc]<rnd[x]) rot(x,0);}
else {insert(rc,p-siz[lc]-1,v);if(rnd[rc]<rnd[x]) rot(x,1);}
upd(x);
}
int query(int x,int k){
if(!x) return 0;
if(k<=siz[lc]) return query(lc,k);
else return max(max(mx[lc],f[x]),query(rc,k-siz[lc]-1));
}
int main()
{
read(n);
for(int i=1,x,y;i<=n;i++){
read(x);
printf("%d\n",ans=max(ans,y=query(rt,x)+1));
insert(rt,x,y);
}
}
法二:从后往前删数
最后1~n每个位置上都有数,把第n个插进去的数删掉,就空出一个空,查第n-1个数的位置的时候就查在线段树上查前缀和为k的位置,查完之后又把那个位置清为0,再查n-2…一直到1,就可以处理出每个数的最终位置,然后BIT求LIS即可。
由于博主太懒所以没有Code