思路: 树状数组
分析:
1 给定一个n个数的序列假设为b数组,那么b[i]表示的是i之前比第i个数大的个数,比如样例的2 1 3对应的b数组是0 1 0,现在要求a数组,已知a数组的值是1~n
2 我们通过b数组可以知道a数组的情况,因为前面的数会影响后面的数,那么我们从后面枚举b数组,这样我们可以知道对于第i个数i-b[i]就是剩下的i个数中的第几大的数,比如第二个样例
0 1 2 0 1 , 那么i为5的时候i-b[i] = 5-1 = 4,说明第5的数是1~n中的第四大的数也就是4,接下来我们删除掉4,i为4的时候i-b[i] = 4-0 = 4也就是第四个数是剩下的第4大的数也就是5,以此类推.....
3 通过2的思路,我们发现很简单,但是我利用vector的时候TLE了,说明我们需要一个更高效率的算法
4 由于n个数是1~n,那么我们初始化一个树状数组treeNum[i] = i;
表示的是前面有i个数比它小,那么我们可以知道树状数组是一个单调递增,为什么呢?因为我们初始化每个点的值都还没被取过,那么区间[1,i]的和为i,所以i越大和越大。
通过第2点的分析我们可以知道,我们能够求出每一个数的排名,也就是前面比它小的个数,那么这个就是树状数组保存的值,所以我们可以通过二分答案来求,我们取了某个数之后就标为true,然后树状数组进行单点更新。
但是我们会遇到一个问题就是比如第二个样例中我们把4和5取完之后我们会发现[1,3]和[1,4]和[1,5]这三个区间的和为3,但是只有3是符合的,因为4和5被取了,所以我们在二分的时候应该注意判断值是否被取过
5 那么我们来分析一下时间复杂度,我们需要枚举b数组为O(n),每次二分的时间为O(logn),每次更新树状数组的时间为O(logn),总的为O(n*logn*logn)
代码:
/***********************************************
* By: chenguolin *
* Date: 2013-08-19 *
* Address: http://blog.csdn.net/chenguolinblog *
***********************************************/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 200010;
int n;
int num[MAXN];
int treeNum[MAXN];
bool isSelect[MAXN];
int lowbit(int x){
return x&(-x);
}
int getSum(int x){
int sum = 0;
while(x){
sum += treeNum[x];
x -= lowbit(x);
}
return sum;
}
void add(int x , int val){
while(x < MAXN){
treeNum[x] += val;
x += lowbit(x);
}
}
void init(){
memset(isSelect , false , sizeof(isSelect));
memset(treeNum , 0 , sizeof(treeNum));
for(int i = 1 ; i <= n ; i++)
add(i , 1);
}
int search(int x){
int left = 1;
int right = n;
while(left <= right){
int mid = (left+right)>>1;
int sum = getSum(mid);
if(sum == x){
if(!isSelect[mid])
return mid;
right = mid-1;
}
else if(sum < x)
left = mid+1;
else
right = mid-1;
}
}
void solve(){
init();
int output[MAXN];
for(int i = n ; i >= 1 ; i--){
int x = i-num[i];
int ans = search(x);
output[i] = ans;
isSelect[ans] = true;
add(ans , -1);
}
printf("%d" , output[1]);
for(int i = 2 ; i <= n ; i++)
printf(" %d" , output[i]);
puts("");
}
int main(){
int cas;
scanf("%d" , &cas);
while(cas--){
scanf("%d" , &n);
for(int i = 1 ; i <= n ; i++)
scanf("%d" , &num[i]);
solve();
}
return 0;
}