zoukankan      html  css  js  c++  java
  • [TJOI2018]最长上升子序列

    动态维护LIS?

    观察题目:在第 i 轮操作时,将数字 i 插入
    插入的数字是当前最大的
    如果答案与上次不同,新的LIS必以 i 结尾
    以 i 结尾的LIS无法再伸长(因为比 i 小的都插入完了)

    也就是说,加入 \(i+1\)\(n\) 的数,不会对以 \(i\) 结尾的上升子序列有影响,所以我们不用去动态地维护LIS的大小,只需要最后把总的序列做一次LIS就好了。然后对于第 \(i\) 个输出,只需要求得分别以 \(1\)\(i\) 结尾的子序列的最大值即可。

    求解LIS

    面对1e5的数据,O(\(n^2\))的大暴力显然是不行的,我们考虑数据结构优化。

    树状数组优化流程:
    1.对原数列以值为关键字排序,记录原来的位置(存入一个结构体)
    2.排完序后,数列的值显然是1n依次递增,我们不妨枚举1n的值。
    对于每一个\(i\),找到它原来的位置记为p,则用树状数组找到在位置p之前的最大值,作为更新的来源。
    3.算完后,把\(i\)这个值加到树状数组\(p\)的这个位置,重复执行2

    代码:

    inline int lowbit(int x){return x&(-x);}
    inline void add(int x,int val){while(x<=n){c[x]=max(c[x],val);x+=lowbit(x);}}
    inline int query(int x){int ret=0;while(x){ret=max(ret,c[x]);x-=lowbit(x);}return ret;}
    
    for(int i=1;i<=n;i++) a[i].num=i;//原位置
    sort(a+1,a+1+n,Cmp);//排序
    int ans=0;
    for(int i=1;i<=n;i++){
        int maxx=query(a[i].num);//查找位置为a[i].num前的最大值
        add(a[i].num,++maxx);//把当前的数加入树状数组
        ans=max(ans,maxx);//取最大值
        printf("%d\n",ans);//输出当前最大值
    }
    

    模拟插入操作

    我们采用Splay来实现此部分的功能
    在这之前请各位精通文艺平衡树
    首先插入两个极大极小的数(为了避免玄学数组越界)
    对于每次插入操作,例如把val插到x位置的后面,就先把x+1位置的数旋转到根节点,再把x+2位置的数旋转到根节点的右儿子,那么你只需要把数加到根节点的右儿子的左儿子(不懂得可以模拟一下),这样就实现了插入操作(其实就是提取区间操作)

    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    #define rint register int
    #define INF 0x3f3f3f3f
    #define N 100007
    
    template<class T>
    inline void read(T &x){
        T flag=1;x=0;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
        x*=flag;
    }
    
    struct Splay{
        int val,fa,s[2],size;
    }t[N];
    struct Node{
        int num,val;
    }a[N];
    
    int c[N];
    int root,T,n=0,cnt=0,m,num=0,f[N];
    
    inline int max(int x,int y){return x>y? x:y;}
    inline bool Cmp(const Node a,const Node b){return a.val<b.val;}
    inline void update(int p){t[p].size=t[t[p].s[0]].size+t[t[p].s[1]].size+1;}
    inline int wich(int x){return t[t[x].fa].s[0]==x? 0:1;}
    inline void connect(int x,int y,int f){t[x].fa=y;t[y].s[f]=x;}
    inline int lowbit(int x){return x&(-x);}
    inline void add(int x,int val){while(x<=n){c[x]=max(c[x],val);x+=lowbit(x);}}
    inline int query(int x){int ret=0;while(x){ret=max(ret,c[x]);x-=lowbit(x);}return ret;}
    
    inline void rotate(int x){
        int y=t[x].fa,rt=t[y].fa;
        int ys=wich(x),rts=wich(y);
        connect(t[x].s[ys^1],y,ys);
        connect(y,x,ys^1);connect(x,rt,rts);
        update(y);update(x);
    }
    
    inline void rota(int p){
        if(wich(p)==wich(t[p].fa)){rotate(t[p].fa);rotate(p);}
        else{rotate(p);rotate(p);}
    }
    
    inline void splay(int p,int to){
        if(!p) return;
        if(p==to) return;
        if(to==root) root=p;
        while(1){
            if(t[p].fa==to){rotate(p);return;}
            if(t[t[p].fa].fa==to){rota(p);return;}
            rota(p);
        }
    }
    
    inline int find(int x){
        rint p=root;
        while(p){
            if(x<=t[t[p].s[0]].size) p=t[p].s[0];
            else if(x==t[t[p].s[0]].size+1) return p;
            else{x-=t[t[p].s[0]].size+1;p=t[p].s[1];}
        }
        return 0;
    }
    
    inline void insert(int val,int k){
        int l=find(k+1),r=find(k+2);
        splay(l,root);
        splay(r,t[root].s[1]);
        t[++cnt]=(Splay){val,t[root].s[1],{0,0},1};
        t[t[root].s[1]].s[0]=cnt;
        update(t[root].s[1]);update(root);
        splay(cnt,root);
    }
    
    inline void dfs(int p){
        if(t[p].s[0]) dfs(t[p].s[0]);
        if(t[p].val!=INF&&t[p].val!=-INF)
            a[++num].val=t[p].val;
        if(t[p].s[1]) dfs(t[p].s[1]);
    }
    
    int main(){
        t[++cnt]=(Splay){-INF,0,{0,2},2};
        t[++cnt]=(Splay){INF,1,{0,0},1};
        read(n);rint x;
        root=1;
        for(int i=1;i<=n;i++)
            read(x),insert(i,x);
        dfs(root);
        for(int i=1;i<=n;i++) a[i].num=i;
        sort(a+1,a+1+n,Cmp);
        int ans=0;
        for(int i=1;i<=n;i++){
            int maxx=query(a[i].num);
            add(a[i].num,++maxx);
            ans=max(ans,maxx);
            printf("%d\n",ans);
        }
    }
    
  • 相关阅读:
    OpenCV 2.4.11 VS2012 Configuration
    [LeetCode] 11. Container With Most Water 装最多水的容器
    Android rxjava2的disposable
    Android在应用设置里关闭权限,返回生命周期处理
    Android 使用greenDAO 3.2.2 操作外部数据库
    Android中intent相关,setFlag(xx);
    Android 关于Acitivity 的setFlag以及launchmode的总结
    GreenDao3.0新特性解析(配置、注解、加密)
    Android RecycleView实现混合Item布局
    Android各大手机系统打开权限管理页面
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/11166322.html
Copyright © 2011-2022 走看看