***********************************************声明******************************************************
原创作品,出自 “晓风残月xj” 博客,欢迎转载,转载时请务必注明出处(http://blog.csdn.net/xiaofengcanyuexj)。
由于各种原因,可能存在诸多不足,欢迎斧正!
*********************************************************************************************************
本题要求根据每个位置逆序数的累计个数,还原数列。可以从后往前推,求出当前位置元素ans[i]的前面有多少元素比他大或比他小,ans[i]=log[i]-log[i-1,注意边界的处理,但时,只知道前面比他大或比他小的元素是不能准确定位元素大小的,还得知道后面比他大或比他小的元素。
1)、前面比他大或小的元素个数可以通过反向遍历O(n)求出。
2)、后面比他大或小的元素个数可以通过在某个数组中标价,然后通过二分结合某个快速求区间和的算法-线段树得出,时间复杂度O(log(N))^2
这样一来,总的时间复杂度为O(N*log(N)) .好久没做题,在没有模板的情况下一棵线段树和二分求第一个等于某个元素的代码调试了很久,看来以后得多敲敲这类基础代码,而不是一味的堆积业务代码,哈哈。
#include
#include
#define MAXN 50000+10
int log[MAXN];
int ans[MAXN];
struct treeNode
{
int l,r,sum;
}tree[MAXN*3];
void build(int id,int l,int r)
{
tree[id].l=l,tree[id].r=r;
if(l==r)
{
tree[id].sum=1;
return ;
}
int mid=(l+r)>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
}
void update(int pos,int id,int val)
{
if(tree[id].l==tree[id].r)
{
tree[id].sum+=val;
return ;
}
int mid=(tree[id].l+tree[id].r)>>1;
if(pos<=mid)
{
update(pos,id<<1,val);
}
else
{
update(pos,id<<1|1,val);
}
tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
}
int query(int l,int r,int id)
{
if(tree[id].l==l&&tree[id].r==r)
{
return tree[id].sum;
}
int mid=(tree[id].l+tree[id].r)>>1;
if(r<=mid)
{
return query(l,r,id<<1);
}
else if(l>mid)
{
return query(l,r,id<<1|1);
}
return query(l,mid,id<<1)+query(mid+1,r,id<<1|1);
}
int main()
{
int t;
scanf("%d",&t);
while(--t)
{
memset(tree,MAXN*sizeof(int),0);
int n;
scanf("%d",&n);
log[0]=0;
for(int i=1;i<=n;++i)
{
scanf("%d",&log[i]);
}
build(1,1,n);
for(int j=n;j>0;--j)
{
int tmp=j-(log[j]-log[j-1])-1;
int l=1,r=n;
while(l>1;
// printf("tmp=%d mid=%d l=%d r=%d\n",tmp,mid,l,r);
if(query(l,mid,1)<=tmp)
{
l=mid+1;
}
else
{
r=mid;
}
}
ans[j]=l;
// printf("%d \n",l);
update(ans[j],1,-1);
}
for(int k=1;k<=n;++k)
{
printf("%d ",ans[k]);
}
printf("\n");
}
return 0;
}