[bzoj3173][树状数组]最长上升子序列

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

题解

在无数次wa后我终于发现我又理解错题意了。。
“表示我们将k插入到位置Xk”,这句话应该这样理解。。
假如原数列中第Xk个位置没有人,则插入至第Xk个,否则插入到第Xk个的前面,而不是替代Xk
拿样例说话,首先全部++
第一次插入 结果1
第二次插入 结果21
第三次插入 结果213…
而不是23
那。。从后往前,二分出每个数应该属于的位置。之后Nlogn的最长上升子序列即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
int s[110000],n;
int lowbit(int x){return x&-x;}
void change_max(int x,int w){while(x<=n)s[x]=max(s[x],w),x+=lowbit(x);}
void change_sum(int x){while(x<=n)s[x]++,x+=lowbit(x);}
int findmax(int x)
{
    int ret=0;
    while(x>=1)ret=max(ret,s[x]),x-=lowbit(x);
    return ret;
}
int findsum(int x)
{
    int ret=0;
    while(x>=1)ret+=s[x],x-=lowbit(x);
    return ret;
}
int a[110000];
int main()
{
    int maxx=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){scanf("%d",&a[i]);a[i]++;}
    for(int i=n;i>=1;i--)
    {
        int l=1,r=n;
        while(l<r)
        {
            int mid=(l+r)/2;
            int tmp=mid-findsum(mid);
            if(tmp<a[i])l=mid+1;
            else r=mid;
        }
        a[i]=l;change_sum(a[i]);
    }
    memset(s,0,sizeof(s));
    int ans=0;  
    for(int i=1;i<=n;i++)
    {
        int tmp=findmax(a[i])+1;
        change_max(a[i],tmp);
        ans=max(ans,tmp);
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值