题目大意
共n次操作,第i次操作在第xi个数后插入数字i并询问当前最长上升子序列。
n<=100000。
离线大法好
我们可以先处理出最终序列,然后做一次最长上升子序列。假设数字i最终位置为a[i],那么对于第i次询问答案就是f[a[i]]。
Treap
如果强制在线该怎么办?
那就是一道Treap裸题了。
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10;
int fix[maxn],key[maxn],num[maxn],size[maxn],tree[maxn][2];
int i,j,k,l,r,t,n,m,ans,root;
void update(int x){
num[x]=max(key[x],max(num[tree[x][0]],num[tree[x][1]]));
size[x]=size[tree[x][0]]+size[tree[x][1]]+1;
}
void split(int x,int y,int &l,int &r){
if (!x) l=r=0;
else{
if (size[tree[x][0]]>=y){
split(tree[x][0],y,l,r);
tree[x][0]=r;
r=x;
}
else{
split(tree[x][1],y-size[tree[x][0]]-1,l,r);
tree[x][1]=l;
l=x;
}
update(x);
}
}
void merge(int l,int r,int &x){
if (!l||!r) x=l+r;
else{
if (fix[l]<fix[r]){
merge(tree[l][1],r,tree[l][1]);
x=l;
}
else{
merge(l,tree[r][0],tree[r][0]);
x=r;
}
update(x);
}
}
int main(){
srand(time(0));
ans=0;
scanf("%d",&n);
root=0;
fo(i,1,n){
scanf("%d",&k);
split(root,k,l,r);
num[i]=key[i]=num[l]+1;
ans=max(ans,key[i]);
fix[i]=rand();
size[i]=1;
merge(l,i,l);
merge(l,r,root);
printf("%d\n",ans);
}
}