有一组乱序的数列,数列有N个,范围是1-N,相同数字不重复。现给定另一组数列,新数列的第k位的值表示在原数列的第k位之前有几个数比其小。求原数列。
用线段树或树状数组+二分可以做,因为这个问题可以转化为求第k大数。
从新数列的最后一位a[n]开始看,因为在之前有a[n]个数比其大,原数列的值一定是a[n]+1。确定一个元素的位置后,从1~N这个序列中删除a[n]+1。 那么到了a[n-1],在删除了a[n]+1后,需要确定a[n-1]+1是第几大的数。这样一直求完,最后倒序输出就可以了。
线段树经典题的题解:http://blog.youkuaiyun.com/sssogs/article/category/1235231
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
struct T
{
int l;
int r;
int mid;
int v;
};
T tr[20000];
void build(int l,int r,int c)
{
tr[c].l=l;
tr[c].r=r;
tr[c].mid=(l+r)>>1;
tr[c].v=r-l+1;
if (l == r)
return ;
build(l,tr[c].mid,c<<1);
build(tr[c].mid+1,r,c<<1|1);
}
void change(int c,int n)
{
tr[c].v--;
if (tr[c].l == n && tr[c].r == n)
return ;
if (tr[c].mid >= n)
change(c<<1,n);
else
change(c<<1|1,n);
}
int query(int c,int n)
{
if (tr[c].l == tr[c].r)
return tr[c].l;
if (tr[c<<1].v > n)
{
query(c<<1,n);
}
else
{
n-=tr[c<<1].v;
query(c<<1|1,n);
}
}
int main()
{
int n,a[8010],i;
scanf("%d",&n);
a[0]=0;
for (i=1; i<n; i++)
{
scanf("%d",a+i);
}
build(1,n,1);
for (i=n-1; i>=0; i--)
{
a[i]=query(1,a[i]);
change(1,a[i]);
}
for (i=0; i<n; i++)
{
printf("%d\n",a[i]);
}
}