P4309 [TJOI2013] 最长上升子序列

P4309 [TJOI2013] 最长上升子序列

题目描述

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

数据范围:

\(100\%\) 的数据 \(n\le10^5\)

说句闲话

应该没有那个神人想在线维护这玩意吧,毕竟两颗平衡树什么的还是太不可爱了。

solution:

我们先把所有操作离线然后用平衡树维护出最终的序列,再将每个数字所在位置映射到一个数组 \(a_i\) 中,表示 \(i\) 在最终序列中的位置。

然后我们思考一下以前我们怎么 \(O(nlogn)\) 求 LIS 的:
维护一个数组,表示当前的 LIS 序列,然后二分一下当前点可以作为 LIS 上的哪个点然后优化一下。

但是我们发现对于本题,如果再用平衡树去维护这个东西貌似不太好写 (反正我debug写炸了,换了线段树)

但是我们发现使用线段树也同样可以做到 \(O(nlogn)\) 求LIS:

我们开一颗线段树,维护一下以某个数为结尾的 LIS 的长度最大值 \(len\) 。那么对于一个数 \(x\) 以该点为结尾的 LIS 长度就是 $$len_i=1+ \max_{i=1}^{x-1} len_i $$ 然后我们再把 \(len_i\) 更新到线段树上就好了。

然后这题就做完了。

Code:

#include<bits/stdc++.h>
const int N=1e5+5;
const int inf=1e9;
using namespace std;
int rd(){return rand()*rand()+17;}
struct FHQ_Treap{
    int rt,cnt;
    int fa[N];
    struct Tree{
        int ls,rs,val,siz,pri;
    }t[N];
    int Node(int val){t[++cnt]={0,0,val,1,rd()};return cnt;}
    void pushup(int x)
    {
        t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1;
        if(t[x].ls)fa[t[x].ls]=x;
        if(t[x].rs)fa[t[x].rs]=x;
    }
    void splite_siz(int x,int &a,int &b,int k)
    {
        if(!x){a=b=0;return ;}int tmp=t[t[x].ls].siz+1;
        if(k>=tmp){a=x;splite_siz(t[x].rs,t[x].rs,b,k-tmp);}
        else {b=x;splite_siz(t[x].ls,a,t[x].ls,k);}
        pushup(x);
    }
    int merge(int x,int y)
    {
        if(!x||!y)return x|y;
        if(t[x].pri<t[y].pri){t[x].rs=merge(t[x].rs,y);pushup(x);return x;}
        else {t[y].ls=merge(x,t[y].ls);pushup(y);return y;}
    }
    int get_siz(int x)
    {
        if(!x)return 0;
        int res=t[t[x].ls].siz+1;
        while(fa[x])
        {
            if(t[fa[x]].rs==x)res+=t[t[fa[x]].ls].siz+1;
            x=fa[x];
        }
        return res;
    }
}T1;
int n,ans;
int a[N];
inline int Max(int x,int y){return x>y ? x : y;}
struct Segment_Tree{
    #define ls x<<1
    #define rs x<<1|1
    struct Tree{
        int val;
    }t[N<<2];
    inline void pushup(int x){t[x].val=Max(t[ls].val,t[rs].val);}
    void upd(int x,int l,int r,int pos,int k)
    {
        if(l==r){t[x].val=Max(t[x].val,k);return;}
        int mid=l+r>>1;
        if(pos<=mid)upd(ls,l,mid,pos,k);
        else upd(rs,mid+1,r,pos,k);
        pushup(x);
    }
    int query(int x,int l,int r,int L,int R)
    {
        if(R<l||r<L)return 0;
        if(L<=l&&r<=R){return t[x].val;}
        int mid=l+r>>1,res=0;
        if(L<=mid)res=Max(res,query(ls,l,mid,L,R));
        if(mid<R) res=Max(res,query(rs,mid+1,r,L,R));
        return res;
    }
}T;
void work()
{
    cin>>n;
    for(int i=1,pos,a,b,c;i<=n;i++)
    {
        scanf("%d",&pos);
        T1.splite_siz(T1.rt,a,b,pos);
        T1.rt=T1.merge(T1.merge(a,T1.Node(i)),b);
    }
    for(int i=1;i<=n;i++)
    {
        a[i]=T1.get_siz(i);
    }
    for(int i=1;i<=n;i++)
    {
        int tmp=T.query(1,1,n,1,a[i])+1;
        ans=Max(ans,tmp);
        T.upd(1,1,n,a[i],tmp);
        printf("%d\n",ans);
    }
}
int main()
{
    //freopen("P4309.in","r",stdin); freopen("P4309.out","w",stdout);
    work();
    return 0;
}
posted @ 2025-01-24 16:12  liuboom  阅读(13)  评论(0)    收藏  举报