HDU 5592 ZYB's Game 【树状数组】+【二分】

<题目链接>

题目大意:

给你一个由1~n,n个数组成的序列,给出他们每个的前缀逆序数,现在要求输出这个序列。

解题分析:

由前缀逆序数很容易能够得到每个数的逆序数。假设当前数是i,它前面比它小的数为a[i]( i - 1 - i的逆序数即可),我们不难知道,i在前i个数中是第i+1大的。然后我们从后往前考虑,每次都能确定一个位置的数的大小,根据当前位置i的数在 1~i 的数的大小,我们用二分查找快速聪当前还未分配的数中给它分配相应大小的数值,然后将这个数值从可分配的数中剔除,防止对前面的数造成影响(相当于每次只考虑i前面的数,i后面的数都已经确定好了数值)。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int N = 5e4+5;
 6 int n;
 7 int a[N],tr[N],ans[N];
 8 inline int lowbit(int x){return x&(-x);}
 9 void add(int x,int val){
10     for(int i=x;i<=N;i+=lowbit(i))
11         tr[i]+=val;
12 }
13 int sum(int x){
14     int ans=0;
15     for(int i=x;i>0;i-=lowbit(i))
16         ans+=tr[i];
17     return ans;
18 }
19 int Binary(int k){
20     int lt = 1, rt = n, mid, t;
21     while (lt+1 < rt){
22         mid = (lt+rt)>>1;
23         t = sum(mid);    
24         if (t >= k) rt = mid;
25         else lt = mid;
26     }
27     if (sum(lt) == k) return lt;
28     else return rt;
29 }
30 void solve(){
31     for(int i=1;i<=n;i++)add(i,1);
32     for(int i=n;i>0;i--){     //从后往前,逐渐分配可供选择的数
33         ans[i]=Binary(a[i]+1);  //在当前可供选择的数中,挑选第a[i]+1大的数
34         add(ans[i],-1);   //因为是根据第i个数是前i个数中第a[i]+1大的来确定位置的,所以要消除i后面的所有元素的影响
35     }
36     for(int i=1;i<=n;i++)
37         printf("%d%s",ans[i],i==n?"\n":" ");
38 }
39 int main(){
40     int T;scanf("%d",&T);while(T--){
41         scanf("%d",&n);
42         int pre=0, now;
43         for(int i=1;i<=n;i++){
44             scanf("%d",&now);
45             a[i]=i-1-(now-pre);
46             pre=now;
47         }
48         memset(tr,0,sizeof(tr));
49         solve();
50     }
51 }

2018-12-15

猜你喜欢

转载自www.cnblogs.com/00isok/p/10123777.html