点击打开SPOJ227
思路: 树状数组
分析:
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; }