• 《算法竞赛进阶指南》0x42树状数组 POJ2182谜一样的牛 树状数组/倍增


    题目链接:https://www.acwing.com/problem/content/245/

    题目给出一个长度为n-1的序列表示一个位置前面有多少个比他小,问这个序列是多少?这个序列是一个1-n的全排列。

    通过从后向前扫描可知每个位置的编号,比如最后一个是an+1,这个位置在之后不考虑,然后从接下来存在的位置中找出第ai+1小的,可以表示成一个01串,将用过的变为0,求第k个1,这个问题就变成了求这个01序列的前缀和中第一个大于等于ai+1的,通过树状数组和二分搜索可以在O(log^2n)时间内求出一个结果,总的时间复杂度是O(nlog^2n)。

    通过倍增算法的话,一次计算时间可以降为O(logn)。倍增算法中很好的利用了二进制的特性以及树状数组中的c[x]管理的是lowbit(x)长度的和,利用二进制位将a[i]分解并且通过最多log(a[i])次叠加计算出小于a[i]的最后一个位置。然后下一个位置就是a[i]的位置。

    注意,倍增算法中,小于等于a[i]的最后一个位置不一定是大于等于a[i]的第一个位置。所以需要计算小于a[i]的最后一个位置。

    代码1:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int maxn = 100010;
    int c[maxn];
    int n;
    int a[maxn];
    int lowbit(int x){
        return x&(-x);
    }
    void update(int x,int C){
        while(x<=n){
            c[x]+=C;
            x+=lowbit(x);
        }
    }
    int query(int x){
        int ans=0;
        while(x){
            ans+=c[x];
            x-=lowbit(x);
        }
        return ans;
    }
    int num[maxn];
    int main(){
    
        cin>>n;
        for(int i=2;i<=n;i++){
            scanf("%d",&a[i]);
            a[i]++;
        }
        a[1]=1;
        for(int i=1;i<=n;i++)c[i]=lowbit(i);
        for(int i=n;i;i--){
            int l=1,r=n;
            while(l<r){//二分求第一个大于等于ai的前缀和 
                int mid=(l+r)>>1;
                if(query(mid)>=a[i])r=mid;
                else l=mid+1;
            }
            num[i]=l;
            update(l,-1);
        }
        for(int i=1;i<=n;i++)printf("%d
    ",num[i]);
        return 0;
    }

    代码2:

    #include<iostream>
    #include<cstdio>
    #include<math.h>
    using namespace std;
    const int maxn =  100010;
    int c[maxn],a[maxn];
    int lowbit(int x){return x&(-x);}
    int n;
    void update(int x,int C){
        while(x<=n){
            c[x]+=C;
            x+=lowbit(x);
        }
    }
    int h[maxn];
    int main(){
        cin>>n;
        for(int i=2;i<=n;i++){
            scanf("%d",&a[i]);
            a[i]++;
        }
        a[1]=1;
        int p[30];
        p[0]=1;
        for(int i=1;i<=25;i++)p[i]=p[i-1]<<1;//预存2次幂 
        for(int i=1;i<=n;i++)c[i]=lowbit(i);
        int t=log(n)/log(2);//最大的2^t<=n 
        for(int i=n;i;i--){
            int sum=0,ans=0;//ans中存放的是二进制位,表示最后一个小于ai的位置 
            for(int j=t;j>=0;j--){
                if(ans+p[j]<=n && sum+c[ans+p[j]]<a[i]){
                    sum+=c[ans+p[j]];
                    ans+=p[j];
                }            
            }
            h[i]=ans+1;
            update(ans+1,-1); 
        }
        for(int i=1;i<=n;i++)printf("%d
    ",h[i]);
        return 0;
    }
  • 相关阅读:
    todo---HttpClient,httpUrlConnection
    todo---callback
    todo-braintree-java
    todo--com.paypal.sdk
    todo--OkHttp基本使用
    @RequestBody 和@ResponseBody 注解详解
    常用Jar包下载
    SpringMVC 使用JSR-303进行校验 @Valid
    SpringMVC 之 表单标签
    SpringMVC 自定义类型转换器
  • 原文地址:https://www.cnblogs.com/randy-lo/p/13296455.html
Copyright © 2020-2023  润新知