/**
* 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;
}
poj2182 线段树/线段数组+二分搜索
最新推荐文章于 2023-06-05 05:05:40 发布