[bzoj3173][TJOI2013]最长上升子序列

题目大意

共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);
 }
}

你可能感兴趣的:([bzoj3173][TJOI2013]最长上升子序列)