BZOJ 3173: [Tjoi2013]最长上升子序列 【在线Treap/离线线段树】

版权声明:本文为博主原创文章,未经博主允许必须转载。 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

猜你喜欢

转载自blog.csdn.net/C20181220_xiang_m_y/article/details/88901288