zoukankan      html  css  js  c++  java
  • 【bzoj3173】【Tjoi2013】【最长上升子序列】treap+dp二分优化

    这里写图片描述
    [pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=61560361
    向大(hei)佬(e)实力学(di)习(tou)

    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

    其实这道题算是一道裸题了,拿来练习一下模板。看到这种活动的插入,就能立即想到平衡树。至于最长上升子序列,如果每插入一次就求一次,再优化都要超时。于是我们就找找性质,发现:由于是有序插入,后插入的一定大,那么每插入一个点,以此点结尾的最长上升子序列就不受后面点的干扰。那么在插入完毕后求一次最长上升子序列就可以了。之后的输出需注意要求的是整个数列的答案,还要在小于等于插入数的dp数组中求max。因为这个我又wa了一遍

    至于二分优化,其实也算是练手。简单梳理一下,也便自己理解更透彻。要开一个单调栈(?),存两个元素,一个为值,一个为下标。
    以 6 2 1 4 3 为例,从左往右扫
    6进栈 >6(1)
    dp[1]=1;
    2比栈顶元素小,要去替换,二分到6,替换>2(2)
    dp[2]=1;
    1同理>1(3)
    dp[3]=1;
    4>1,4直接进栈>1(3) 4(4)
    dp[4]=dp[3]+1=2;
    3二分换4,>1(3) 3(5)
    dp[5]=dp[3]+1=2;
    最后再for一遍找max,总o(log n)

    这道题因为比较特殊,数字不重复,且按序插入,所以直接以数字本身作为下标。

    求最长上升子序列还可以用树状数组优化,哪天再遇到这样的题再复习树状数组优化吧!

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std ;
    
    const int N=100000+5;
    
    int n,x;
    struct Node{
        Node *ch[2];
        int v,r,siz;
        int cmp(int x){
            return x <= ch[0]->siz ? 0 : 1;
        }
    }*root,*null,pool[N],*tail=pool;
    int c[N],top=0,dp[N];
    
    Node *newnode(){
        Node *rt=++tail;
        rt->ch[0]=rt->ch[1]=null;
        rt->v=rt->r=rt->siz=0;
        return rt;
    }
    void rotate(Node *&nd,int d){
        Node *tp=nd->ch[d];
        nd->ch[d]=tp->ch[d^1];tp->ch[d^1]=nd;
        nd->siz=nd->ch[0]->siz+nd->ch[1]->siz+1;
        tp->siz=tp->ch[0]->siz+tp->ch[1]->siz+1;
        nd=tp;
    }
    void insert(Node *&nd,int val,int pos){
        if(nd==null){
            nd=newnode();
            nd->v=val;
            nd->siz=1;
            nd->r=rand();
            return ;
        }
        int d=nd->cmp(pos);
        insert(nd->ch[d],val,d ? pos - nd->ch[0]->siz - 1: pos );
        if(nd->ch[d]->r > nd->r ){
            rotate(nd,d);
        } 
        nd->siz=nd->ch[0]->siz+nd->ch[1]->siz+1;
    }
    int get_dp(int x){
        int rt;
        if(x>c[top]){
            c[++top]=x;
            return dp[c[top-1]]+1;
        } 
        int le=0,ri=top;
        while(le<ri){
            int mid=(le+ri)>>1;
            if(c[mid]>x) ri=mid;
            if(c[mid]<=x) le=mid+1;
        }
        c[le]=x;
        return dp[c[le-1]]+1;
    }
    void query(Node *nd){
        if(nd==null) return ;
        query(nd->ch[0]);
        dp[nd->v]=get_dp(nd->v);
        query(nd->ch[1]);
    }
    int main(){
        srand(23425546);
        null=++tail;
        null->ch[0]=null->ch[1]=null;
        null->v=null->r=null->siz=0;
        root=null;
    
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&x);
            insert(root,i,x);
        }
        query(root);
        for(int i=1;i<=n;i++){
            dp[i]=max(dp[i-1],dp[i]);
            printf("%d
    ",dp[i]);
        }
        return 0;
    }

    总结:
    1、对题的辨析能力还需加强,要多想多观察,找到不被影响的东西

  • 相关阅读:
    uni-app 发起请求,Toast 消息提示 ,跳转页面
    uView初识
    uni-app初识
    docker目录 /var/lib/docker/containers 日志清理
    Linux中使用pigz工具更快的压缩和解压文件
    docker 修改默认网段
    LayaAir提示:版本不匹配!全局tsc(2.7.2)!=VS Code的语言服务(2.1.5)。可能出现不一致的编译错误
    C++ 格式化 浮点为字符串
    安装 ta-lib
    编译 python 代码
  • 原文地址:https://www.cnblogs.com/LinnBlanc/p/7763155.html
Copyright © 2011-2022 走看看