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 
#include 
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=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= 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;
}

你可能感兴趣的:(poj)