题意:
求出一个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; }