试题链接

题意:

求出一个1到n的排列,这个排列只告诉了你从第二个数字开始的比这个数字小的数的个数。

题解:

如果一个数p,p的前面有m个比他小的数,后面有n个比他小的数,那p的位置就是n+m+1
我们可以二分这个p,如果m+n+1>mid,mid就取小了;否则就取大了
p前面有多少比他小的数,在输入答案时就知道了
p后面有多少比他小的数,可以用树状数组来搞定
我们在处理时应该倒序处理,想想最后一个数的真实值就是a[n]+1,然后删除最后一个数,看倒数第二个,一次类推即可

代码:

# include<cstdio>
# include<iostream>
# include<fstream>
# include<algorithm>
# include<functional>
# include<cstring>
# include<string>
# include<cstdlib>
# include<iomanip>
# include<numeric>
# include<cctype>
# include<cmath>
# include<ctime>
# include<queue>
# include<stack>
# include<list>
# include<set>
# include<map>

using namespace std;

const double PI=4.0*atan(1.0);

typedef long long LL;
typedef unsigned long long ULL;

# define inf 999999999
# define MAX 8000+4

int a[MAX];
int ans[MAX];
int tree[MAX];

int n;
int lowbit(int x)
{
    return x&(-x);
}
int getsum ( int pos )//后面比他小的数字的个数 
{
    int ans = 0;
    while ( pos > 0 )
    {
        ans+=tree[pos];
        pos-=lowbit(pos);
    }
    return ans;
}

void update ( int pos,int val )
{
    while ( pos <= n )
    {
        tree[pos]+=val;
        pos+=lowbit(pos);
    }
}

int my_search ( int k )
{
    int left = 1, right = n;
    while ( left < right )
    {
        int mid = ( left+right )>>1;
        int num = getsum (mid);
        if ( mid - 1 < num+k )
        {
            left = mid+1;
        }
        else
        {
            right = mid;
        }
    }
    return left;
}

int main(void)
{
    while ( scanf("%d",&n)!=EOF )
    {
        memset(tree,0,sizeof(tree));
        memset(ans,0,sizeof(ans));
        a[1] = 0;
        for ( int i = 2;i <= n;i++ )
            scanf("%d",&a[i]);
        for ( int i = n;i >= 1;i-- )
        {
            int k = my_search(a[i]);
            update(k,1);//每确定一个位置,进行更新 
            ans[i] = k;
        }

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



    return 0;
}