bzoj3173 [Tjoi2013]最长上升子序列 dp Treap

15 篇文章 0 订阅
4 篇文章 0 订阅

bzoj3173 [Tjoi2013]最长上升子序列
题意:依此插入1-n 求每插完一次的LIS
分析:每个数是按照升序插入的,因此每新加入一个数,不会影响之前的答案,那么我们就可以求出最后的序列,这样我们可以求出以每个数为结尾的LIS,那么答案 ans[i]=max(ans[i],ans[i-1]).
LIS现学的囧……理解是挺好理解的……实现囧……
d[len]:长度为len的LIS的结尾的最小值
d[i]=max(j)+1 其中d[j] < a[i]
d单调 二分O(nlogn)
插入得到最后序列 Treap
记:
iteratorlower_bound( const key_type &key ): 返回一个迭代器,指向键值>= key的第一个元素。
iterator upper_bound( const key_type &key ):返回一个迭代器,指向键值>key的第一个元素。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define N 100005
#define mp make_pair
#define pa pair<int,int>
#define inf 1<<30
using namespace std;
struct Node{int ls,rs,v,key,sz;}t[N];
void upd(int k){t[k].sz=t[t[k].ls].sz+t[t[k].rs].sz+1;}
int num,root,n,x,cnt,a[N],d[N],ans[N],da[N];
int merge(int a,int b){
    if(a==0||b==0) return a+b;
    if(t[a].key<t[b].key){t[a].rs=merge(t[a].rs,b);upd(a);return a;}
    else {t[b].ls=merge(a,t[b].ls);upd(b);return b;}
}
pa split(int k,int x){
    if(x==0) return mp(0,k);
    int ls=t[k].ls,rs=t[k].rs;pa tmp;
    if(t[ls].sz==x) {t[k].ls=0;upd(k);return mp(ls,k);}
    if(t[ls].sz==x-1) {t[k].rs=0;upd(k);return mp(k,rs);}
    if(t[ls].sz>x) {tmp=split(ls,x);t[k].ls=tmp.second;upd(k);return mp(tmp.first,k);}
    if(t[ls].sz<x-1) {tmp=split(rs,x-t[ls].sz-1);t[k].rs=tmp.first;upd(k);return mp(k,tmp.second);}
}
void ins(int x,int v){
    int k=x-1;
    t[++num].ls=0;t[num].rs=0;t[num].sz=1;
    t[num].v=v;t[num].key=rand();
    pa tmp=split(root,k);
    root=merge(tmp.first,num);
    root=merge(root,tmp.second);
}
void dfs(int i){
    if(!i) return;
    dfs(t[i].ls);
    a[++cnt]=i;
    dfs(t[i].rs);
}
void getans(){
    memset(d,127,sizeof(d));
    d[0]=-inf;d[1]=a[1];ans[a[1]]=1;
    int len=1;
    for(int i=2;i<=n;i++){
        int pos=upper_bound(d,d+len,a[i])-d;
        if(a[i]>d[len]) d[++len]=a[i],ans[a[i]]=len;
        else d[pos]=min(d[pos],a[i]),ans[a[i]]=pos;
    }
    for(int i=1;i<=n;i++) ans[i]=max(ans[i],ans[i-1]);
    for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&x),ins(x+1,i);
    dfs(root);
    getans();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值