BZOJ 3173: [Tjoi2013]最长上升子序列

版权声明:本文为一名蒟蒻的原创文章,大神转载的话顺便说个出处呗。 https://blog.csdn.net/cgh_Andy/article/details/78759247

treap做法太裸了 不打了(我哪会treap啊。。懒得打splay 233)

发现一种很妙的离线做法
树状数组 一开始每个位置都为1 从后往前,找到前缀和等他要加到的位置+1的位置,这个位置就是这个值最后的位置(不信你手玩一下) 然后把这个位置值变成0。
上面一步用树状数组上二分这个技巧实现即可。

然后怎么做? dp就好了。。(也用树状数组

所以代码就又短又快了233

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+2;
char O[1<<14],*S=O,*T=O;
#define gc (S==T&&(T=(S=O)+fread(O,1,1<<14,stdin),S==T)?-1:*S++)
inline int read(){
    int x=0,f=1; char ch=gc;
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1; ch=gc;}
    while(ch>='0' && ch<='9'){x=(x<<1)+(x<<3)+(ch^48); ch=gc;}
    return x*f;
}
int n,f[N],a[N],t[N];
int getk(int k){
    int cnt=0,w=0;
    for(int i=20;~i;--i){
        w+=1<<i;
        if(w>n || t[w]+cnt>=k)w-=1<<i;
        else cnt+=t[w];
    }
    return w+1;
}
int get(int x){int u=0; for(;x;x-=x&-x)u=max(u,t[x]); return u;}
int main(){
    n=read(); int i,j;
    for(i=1;i<=n;++i){
        for(j=i;j<=n;j+=j&-j)++t[j]; a[i]=read()+1;
    }
    for(i=n;i;--i){
        int j=getk(a[i]); a[i]=j;
        for(;j<=n;j+=j&-j)--t[j];
    }
    for(i=1;i<=n;++i){
        f[i]=get(a[i])+1;
        for(j=a[i];j<=n;j+=j&-j) t[j]=max(t[j],f[i]);
        printf("%d\n",f[i]=max(f[i],f[i-1]));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/cgh_Andy/article/details/78759247