poj 2182 树状数组+二分

题目:点击打开链接

题意:知道一群牛从1~n-1范围内第i头牛前面编号比自己小的数量,以此来推断每个位置牛的编号

做不粗来啊,看了一下午的树状数组,还是无能为力啊,等着看看线段树,用线段树做一遍吧

来自大神的博客:点击打开链接

  • 分析:由给定的数据我们从后往前进行求解s[n-1],s[n-2]...s[0] 
  • 当我们求解s[k]的时候,由于s[k+1]...s[n-1]已求到,所以只要确定了s[k] 
  • 就能确定s[k+1]~s[n-1]中比s[k]小的个数num,从而k-1-num就是s[k]前面比s[k]小的个数 
  • 如果s[k]-1-num == a[k](既输入的值),则该点可以是s[k],而如何确定s[k]呢?在这可以用二分来确定s[k] 
  • 如何确定num呢?在这用树状数组来求num 
  • 二分时: 
  • 如果s[k]-1-num>=a[k]则right=mid,表示s[k]可以继续变小 
  • 否则left=mid+1,表示s[k]必须变大才可能满足 
  • 注意到这里的s[k]-1-num>=a[k]则right=mid,为什么不直接s[k]-1-num == a[k]时直接得到s[k]呢? 
  • 解释:这是因为满足的s[k]可能不止一个值 
  • 比如: 
  • 5       1      2 
  • 1       3      4 
  • 2   =>  5 或者 5 
  • 1       3      3 
  • 0       1      1 
  • 显然第一种求到的结果也满足输入的值但是却不正确 
  • 所以s[k]-1-num>=a[k]则right=mid,这样假如s[k]满足并且原来s[k+1]~s[n-1]已经有s[k]的话 
  • 那么s[k]-1也一定满足,就会一直right=mid,直到s[k+1]~s[n-1]没有s[k]并且s[k]满足,这个s[k]就是需要求得s[k]



    #include<iostream>
    #include<string.h>
    #include<stdio.h>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int MAX=8005;
    int n,c[MAX],a[MAX];
    int lowbit(int x)
    {
        return x&(-x);
    }
    void add(int x)
    {
        while(x<=n){
            c[x]+=1;
            x+=lowbit(x);
        }
    }
    int sum(int x)
    {
        int s=0;
        while(x>0){
            s+=c[x];
            x-=lowbit(x);
        }
        return s;
    }
    int Search(int x)
    {
        int left=1,right=n,mid;
        while(left<right){
            mid=left+right>>1;
            int num=sum(mid);
            if(mid-1-num>=x)right=mid;
            else left=mid+1;
        }
        return left;
    }
    int main()
    {
        cin>>n;
        for(int i=1;i<n;i++)cin>>a[i];
        a[0]=0;
        for(int i=n-1;i>=0;i--){
            int x=Search(a[i]);
            a[i]=x;
            add(x);
        }
        for(int i=0;i<n;i++)
            printf("%d\n",a[i]);
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值