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);
        }
    }
    
  • 相关阅读:
    LeetCode 275. H-Index II
    LeetCode 274. H-Index
    LeetCode Gray Code
    LeetCode 260. Single Number III
    LeetCode Word Pattern
    LeetCode Nim Game
    LeetCode 128. Longest Consecutive Sequence
    LeetCode 208. Implement Trie (Prefix Tree)
    LeetCode 130. Surrounded Regions
    LeetCode 200. Number of Islands
  • 原文地址:https://www.cnblogs.com/Tieechal/p/11247397.html
Copyright © 2011-2022 走看看