poj2182 线段树/线段数组+二分搜索

/**
 * poj2182 线段树
 * 我在这里先做了一个n^2的算法,就是比较直观的,从后往前
 * 最开始是最后一个数,比它小的数有k个,那最后一个数就是k+1
 * 那推广,第i个数,它前面比它小的个数为kk,再加上之前后面已经出现的比它小的个数pp加起来,再加1,那这个第i个数就是kk+pp+1
 * 最后剩下一个数就是第一个数
 *
 *
 * 再学习一下别人的线段树的方法
 * 线段树是说,将整个区间分成一段一段的,每一段都可以计算一个值。树的每一个点都用左区间l和右区间r作为标记。这样来看,线段树比树状数组方法要更多一些,更灵活一些
 * 我们暂不考虑delete方法,只是考虑树的插入和查找,都是logn级别的复杂度
 * 这里我们用线段树的num字段表示某个线段内,还没有处理过的值有几个,这就包括了整个区间
 * 初始化的时候,num字段就是区间内元素的个数
 * 从后往前,思路和最原始算法的思路是一致的,就是利用线段树,查找所有数里面,前面的数(从1开始到自身)还没有被处理的数的个数,与输入的值+1相等的就可以确定这个数了
 * 
 * 有了n^2算法以后可以用树状数组+二分搜索的方法将其优化到nlogn级别
 * 这个算法比线段树的算法时间稍微长了一点,但是空间节省了,思路和线段树算法基本一致,用树状数组记录前k个数中还没有被处理的值
 * 用二分搜索找到值与输入值一致,且最左边的那个值就是我们要找的值了,找到以后将最底端的这个值改成0,向上一路改过去
 * 从后往前循环,就成了
 */
#include <cstdio>
#include <cstring>
const int MAX_NUM = 8001;
//原始n^2算法
int input[MAX_NUM],output[MAX_NUM];
/*
bool flag[MAX_NUM];
int main(){
    int n;
    memset(flag,0,sizeof(flag));
    memset(output,0,sizeof(output));
    scanf("%d",&n);
    int i,j,k;
    for(i=0;i<n-1;++i){
        scanf("%d",&input[i]);
    }

    for(i=n-1;i>=1;--i){
        int count = 0;
        for(j=1;j<=n;++j){
            if(flag[j]){
                count++;
            }
            else if(j == count+input[i-1]+1){
                output[i] = j;
                break;
            }
        }
        flag[j] = true;
    }

    for(j=1;j<=n;++j){
        if(!flag[j]){
            output[0] = j;
            break;
        }
    }
    
    for(i=0;i<n;++i){
        printf("%d\n",output[i]);
    }
    return 0;
}*/

//借鉴了网上代码的线段树方法
/*
struct node{
    int l;
    int r;
    int num;//计算它的范围内还有多少个数没有确定(包括自己)
} nod[MAX_NUM*2];

void build_tree(int idx,int l,int r){
    nod[idx].l = l;
    nod[idx].r = r;
    nod[idx].num = r-l+1;

    if(l==r){
        return ;
    }
    int m = (l+r)/2;
    build_tree(2*idx+1,l,m);
    build_tree(2*idx+2,m+1,r);
}

//query函数最终返回的是这个数之前有多少个数没有被确定(包括自己)
int query(int idx,int dest){
    --nod[idx].num;
    //确定到底,返回准确的坐标值
    if(nod[idx].l == nod[idx].r){
        return nod[idx].l;
    }
    //确定目标在左子树上,向左子树递归
    if(nod[2*idx+1].num >= dest){
        return query(2*idx+1,dest);
    }
    //确定目标在右子树上,将目标值减去左子树上剩余的值(只搜索右子树的部分)
    else{
        dest-=nod[2*idx+1].num;
        return query(2*idx+2,dest);
    }
}


int main(){
    int n;
    scanf("%d",&n);
    build_tree(0,1,n);

    input[1] = 1;
    for(int i=2;i<=n;++i){
        scanf("%d",&input[i]);
        ++input[i];
    }

    for(int i=n;i>0;--i){
        output[i] = query(0,input[i]);
    }

    for(int i=1;i<=n;++i){
        printf("%d\n",output[i]);
    }
    return 0;
}
*/

//线段数组+二分搜索

int lowbit(int x){
    return x&(-x);
}

int raw[MAX_NUM];//记录还没有被处理过的值

void build_array(int n){//将底层的值都暂时置为1
    for(int i=1;i<=n;++i){
        raw[i] = lowbit(i);
    }
}

int sum(int idx){
    int ans=0;
    while(idx>0){
        ans+=raw[idx];
        idx-=lowbit(idx);
    }
    return ans;
}

int search(int n,int dest){
    //二分搜索找到值,即值为dest且为最前面的值
    int l=1,r=n,m;
    while(l!=r){
        m = (l+r)>>1;
        if(sum(m) >= dest){
            r = m;
        }
        else{
            l = m+1;
        }
    }
    return l;
}

void update(int n,int idx){
    while(idx<=n){
        raw[idx]--;
        idx+=lowbit(idx);
    }
}

int main(){

    int n;
    scanf("%d",&n);

    build_array(n);

    input[1] = 1;
    for(int i=2;i<=n;++i){
        scanf("%d",&input[i]);
        ++input[i];
    }


    for(int i=n;i>0;--i){
        output[i] = search(n,input[i]);
        update(n,output[i]);
    }


    for(int i=1;i<=n;++i){
        printf("%d\n",output[i]);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值