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

Posted randy-lo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《算法竞赛进阶指南》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;
}

 

以上是关于《算法竞赛进阶指南》0x42树状数组 POJ2182谜一样的牛 树状数组/倍增的主要内容,如果未能解决你的问题,请参考以下文章

《算法竞赛进阶指南》0x11栈 单调栈求矩形面积 POJ2559

《算法竞赛进阶指南》0x02 POJ2889 分形

《算法竞赛进阶指南》0x43线段树 扫描线算法 POJ2482

《算法竞赛进阶指南》0x27A* 八数码问题 POJ1077

算法竞赛进阶指南扩展最大子段和POJ1050ToTheMax

《算法竞赛进阶指南》0x25广度优先搜索 POJ3322 Bloxorz I