bzoj3173 最长上升子序列 题解--Treap+nlogn求LIS

Description


 

给定一个序列,初始为空。现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置。每插入一个数字,我们都想知道此时最长上升子序列长度是多少?

Input


 

第一行一个整数N,表示我们要将1到N插入序列中,接下是N个数字,第k个数字Xk,表示我们将k插入到位置Xk(0<=Xk<=k-1,1<=k<=N)

Output


 

N行,第i行表示i插入Xi位置后序列的最长上升子序列的长度是多少。

Sample Input


 

3
0 0 2

Sample Output


 

1
1
2

HINT


 

100%的数据 n<=100000

 

题解


 

树的部分比较好理解,读到了位置insert即可,注意插入的时候关键字是cnt,也就是当前数的值。

然后LIS的部分今天才学会,就是nlogn的办法,如果不用stl的话就是二分查找,网上详解很多,在此不多说。

 

代码


#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
int v,n,dp[210000],b[210000],d[210000],cnt,tot,ans[210000],mx,root,p;
struct treap{
    int ls,rs,pri,siz,val;
}a[200010];
void pushup(int o){
    a[o].siz=a[a[o].ls].siz+a[a[o].rs].siz+1;
    return;
}
void lturn(int &o){
    int t=a[o].rs;
    a[o].rs=a[t].ls;
    a[t].ls=o;
    a[t].siz=a[o].siz;
    pushup(o);
    o=t;
}
void rturn(int &o){
    int t=a[o].ls;
    a[o].ls=a[t].rs;
    a[t].rs=o;
    a[t].siz=a[o].siz;
    pushup(o);
    o=t;
}
void insert(int p,int &o){
    if(!o){
        o=++cnt;
        a[o]=(treap){0,0,rand(),1,1};
        return;
    }
    a[o].siz++;
    //
    if(p<=a[a[o].ls].siz){
        insert(p,a[o].ls);
        if(a[o].pri<a[a[o].ls].pri)rturn(o);
    }
    else {
        insert(p-a[a[o].ls].siz-1,a[o].rs);
        if(a[o].pri<a[a[o].rs].pri)lturn(o);
    }
}
void dfs(int o){
    if(a[o].ls!=0)dfs(a[o].ls);
    b[++tot]=o;
    if(a[o].rs!=0)dfs(a[o].rs);
}
void getlis(){
    memset(d,0x3f,sizeof(d));
    d[0]=-1000000;
    for(int i=1;i<=tot;i++){
        int t=upper_bound(d,d+mx+1,b[i])-d;
        if(d[t-1]<=b[i])d[t]=min(d[t],b[i]);
        ans[b[i]]=t;
        mx=max(mx,t);
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&p);
        insert(p,root);
    }
    dfs(root);
    getlis();
    for(int i=1;i<=n;++i){
        ans[i]=max(ans[i-1],ans[i]);
        printf("%d\n",ans[i]);
    }
    return 0;
}

 

 

 

posted @ 2017-09-19 14:35  Requiescat  阅读(144)  评论(0编辑  收藏  举报