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

2021-04-18 04:28

阅读:636

标签:复杂度   ios   pac   搜索   二进制   math   void   for   倍增   

题目链接: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
#includeusing 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(xn){
        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){
        scanf("%d",&a[i]);
        a[i]++;
    }
    a[1]=1;
    for(int i=1;ilowbit(i);
    for(int i=n;i;i--){
        int l=1,r=n;
        while(l//二分求第一个大于等于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"%d\n",num[i]);
    return 0;
}

代码2:

#include
#include
#includeusing 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(xn){
        c[x]+=C;
        x+=lowbit(x);
    }
}
int h[maxn];
int main(){
    cin>>n;
    for(int i=2;i){
        scanf("%d",&a[i]);
        a[i]++;
    }
    a[1]=1;
    int p[30];
    p[0]=1;
    for(int i=1;i25;i++)p[i]=p[i-1]1;//预存2次幂 
    for(int i=1;ilowbit(i);
    int t=log(n)/log(2);//最大的2^t
    for(int i=n;i;i--){
        int sum=0,ans=0;//ans中存放的是二进制位,表示最后一个小于ai的位置 
        for(int j=t;j>=0;j--){
            if(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"%d\n",h[i]);
    return 0;
}

 

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

标签:复杂度   ios   pac   搜索   二进制   math   void   for   倍增   

原文地址:https://www.cnblogs.com/randy-lo/p/13296455.html


评论


亲,登录后才可以留言!