zoukankan      html  css  js  c++  java
  • [51nod-1364]最大字典序排列

    [51nod-1364]最大字典序排列

    Online Judge:51nod-1364

    Label:线段树,树状数组,二分

    题目描述

    题解:

    根据题意很容易想到60%数据的(O(N^2logN))暴力做法,即每次从大数往小数找,如果它能在m步内换到当前位置就把它换到前面去,然后再把选中的位置设为0,可以用树状数组在(O(logN))完成。

    0.O(N^2logN)暴力

    cin>>n;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x,pos[x]=i;
    }
    for(int i=1;i<=n;i++){
        for(int j=n;j>=1;j--){
            if(已经选择过j)continue;
            int times=sum(pos[j])-1;
            if(times<=m){
                选择j;m-=times;
                update(pos[j],-1);
                printf("%d
    ",j);
                break;
            }
        }
    }
    

    对于100%数据考虑优化,思路还是一样的。

    1.打离线赛时的做法

    上面做法的时间瓶颈在于求(times<=m)的最大的数字,由于对于序列来说times递增,发现可以先用二分查找配合树状数组找出一个位置的范围,那么在这个范围内再去查找最大值即可,找最大值可以用线段树实现,然后当你选中了这个最大值后,在树状数组中修改,同时也在线段树中进行单点修改,并维护区间最大值。

    这个做法还是比较无脑的,树状数组存某一位前面还剩余的数字个数,线段树存序列中某段区间还留着的数字的最大值,但好打是重点,由于还嵌套了二分查找,时间复杂度为(O(N*logN*logN))

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+10;
    struct node{
        int l,r,w;
    }b[N<<2];
    int n,m;
    int a[N],id[N],ans[N],c[N];
    inline int lowbit(int x){return x&(-x);}
    void add(int x,int d){
        while(x<=n){
            c[x]+=d;
            x+=lowbit(x);
        }
    }
    int sum(int x){
        int ret=0;
        while(x){
            ret+=c[x];
            x-=lowbit(x);
        }
        return ret;
    }
    void build(int l,int r,int o){
        b[o].l=l,b[o].r=r;
        if(l==r){
            b[o].w=a[l];
            return;
        }
        int mid=l+r>>1;
        build(l,mid,o<<1);
        build(mid+1,r,o<<1|1);
        b[o].w=max(b[o<<1].w,b[o<<1|1].w);
    }
    void update(int o,int pos,int d){
        if(b[o].l==pos&&b[o].r==pos){
            b[o].w=d;
            return;
        }
        int mid=b[o].l+b[o].r>>1;
        if(pos<=mid)update(o<<1,pos,d);
        else update(o<<1|1,pos,d);
        b[o].w=max(b[o<<1].w,b[o<<1|1].w);
    }
    int query(int l,int r,int o){
        if(b[o].l==l&&b[o].r==r)return b[o].w;
        int mid=b[o].l+b[o].r>>1;
        if(r<=mid)return query(l,r,o<<1);
        else if(l>mid)return query(l,r,o<<1|1);
        return max(query(l,mid,o<<1),query(mid+1,r,o<<1|1));
    }
    int main(){ 
    //  freopen("arrange.in","r",stdin);freopen("arrange.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),id[a[i]]=i;
        build(1,n,1);   
        for(int i=1;i<=n;i++)add(i,1);
        for(int i=1;i<=n;i++){
            int l=1,r=n,lim=l;//二分查找范围
            while(l<=r){
                int mid=l+r>>1;
                if((sum(mid)-1)<=m)l=mid+1,lim=mid;
                else r=mid-1;
            }   
            int p=query(1,lim,1);//线段树找
            update(1,id[p],0);//线段树单点修改
            m-=(sum(id[p])-1);
            add(id[p],-1);
            ans[i]=p;   
        }   
        for(int i=1;i<=n;i++)printf("%d
    ",ans[i]);
    }
    

    2.赛后一种较为简洁的做法

    树状数组中的个数也放在线段树上维护,不要了。

    二分查找放在线段树上直接查找,不要了。

    所以只要一个线段树就好了,维护两个东西,区间个数s、区间最值mx。时间复杂度为(O(NlogN))

    代码如下☞

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1e5+10;
    #define For(a,b,c) for(register int a=b;a<=c;a++)
    inline int read(){
        int x=0;char c=getchar();
        while(c<'0'||c>'9')c=getchar();
        while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
        return x;
    }
    int s[N<<2],mx[N<<2],pos[N],id[N],m,n;
    void build(int o,int l,int r){
        if(l==r){
            s[o]=1,mx[o]=id[l];
            return;
        }
        int mid=l+r>>1;
        build(o<<1,l,mid),build(o<<1|1,mid+1,r);
        mx[o]=max(mx[o<<1],mx[o<<1|1]);
        s[o]=s[o<<1]+s[o<<1|1];
    }
    void update(int o,int l,int r,int pos,int co){
        if(l==r){
            m-=co,mx[o]=s[o]=0;
            return;
        }
        int mid=l+r>>1;
        if(pos<=mid)update(o<<1,l,mid,pos,co);
        else update(o<<1|1,mid+1,r,pos,co+s[o<<1]);
        mx[o]=max(mx[o<<1],mx[o<<1|1]); 
        s[o]=s[o<<1]+s[o<<1|1];
    }
    int query(int o,int l,int r,int d){
        if(d<=0)return 0;
        if(l==r)return mx[o];
        int mid=l+r>>1;
        if(d<=s[o<<1])return query(o<<1,l,mid,d);
        return max(mx[o<<1],query(o<<1|1,mid+1,r,d-s[o<<1]));
    }
    int main(){
        n=read(),m=read();
        for(int i=1;i<=n;i++)id[i]=read(),pos[id[i]]=i;
        build(1,1,n); 
        for(int i=1;i<=n;i++){
            int num=query(1,1,n,min(m+1,s[1]));
            printf("%d
    ",num);
            update(1,1,n,pos[num],0);
        }
    }
    
  • 相关阅读:
    pandas replace 替换功能function
    pandas 处理文本数据
    vue-cli3使用bootstrap4
    Sass和less的区别是什么?用哪个好
    vue项目首屏加载优化实战
    NodeJS 框架一览
    pre -commit hook failed (add --no-verify to bypass)
    安装与卸载vue cli3
    vue-cli3.0之vue.config.js的配置项(注解)
    Sass-颜色运算
  • 原文地址:https://www.cnblogs.com/Tieechal/p/11247397.html
Copyright © 2011-2022 走看看