ZYB's Premutation(树状数组+二分)
分析:我们可以逆向考虑(因为正向的话由于第一位的逆序对数一定是0,算不出什么),对于第i个数,它使逆序对的数量增加了temp=num[i]-num[i-1],即区间【1,i-1】内比这个数大的有temp个,即它在i个数中从小到大排在(i-temp)个,那么找到这个数即可。
对于答案序列来讲,他是一个全排列的一种情况,对于用过了的数肯定就不再使用了。那么接下来的任务就是在剩余的数中,挑取第i-(num【i】-num【i-1】)大的数,作为这个位子上的答案;
那么这部分我们可以用树状数组+二分来完成任务。
对于树状数组,我们初始化每个位子上的val都是1.表示每个数字都还没被用过。那么getsum(i)的操作,就是在查找【1,i】这些数字中还剩余多少个数。
那么我们考虑二分这个最终位子pos,使得【1,pos】中剩余的数字为i-(num【i】- num【i-1】)个即可。
那么很容易理解,ans【i】=pos;
对应每一次找到一个答案之后,对应updata(pos,-1)归零即可,表示这个位子上的这个数字我们已经用过了。
AC_Code:
1 #include <iostream> 2 #include <string> 3 #include <cstring> 4 #include <cstdio> 5 #include <algorithm> 6 using namespace std; 7 typedef long long ll; 8 #define lowbit(x) ((x)&(-x)) 9 #define rep(i,first,last) for(int i=first;i<=last;i++) 10 #define dep(i,first,last) for(int i=first;i>=last;i--) 11 const int maxn=5e4+10; 12 const int inf=0x3f3f3f3f; 13 14 int num[maxn],tree[maxn],ans[maxn],n; 15 16 void updata(int i,int k){ 17 while( i<=n ){ 18 tree[i]+=k; 19 i+=lowbit(i); 20 } 21 } 22 23 int getsum(int i){ 24 int res=0; 25 while( i>0 ){ 26 res+=tree[i]; 27 i-=lowbit(i); 28 } 29 return res; 30 } 31 32 int solve(int k){ 33 int ans=0; 34 int l=1; 35 int r=n; 36 while( l<=r ){ 37 int mid=(l+r)>>1; 38 if( getsum(mid)>=k ){ 39 ans=mid; 40 r=mid-1; 41 } 42 else l=mid+1; 43 } 44 return ans; 45 } 46 47 int main() 48 { 49 int T; 50 scanf("%d",&T); 51 while( T-- ){ 52 memset(tree,0,sizeof(tree)); 53 scanf("%d",&n); 54 rep(i,1,n){ 55 updata(i,1); 56 scanf("%d",&num[i]); 57 } 58 dep(i,n,1){ 59 int temp=num[i]-num[i-1]; 60 temp=i-temp; 61 ans[i]=solve(temp); 62 updata(ans[i],-1); 63 } 64 rep(i,1,n-1){ 65 printf("%d ",ans[i]); 66 } 67 printf("%d\n",ans[n]); 68 } 69 return 0; 70 }