倒推序列,如b[i]0 1 0(前面有几个数比b[i] 大)推a[i]2 1 3 ,树状数组 SPOJ 227 Ordering the Soldiers

ORDERS - Ordering the Soldiers

no tags 

As you are probably well aware, in Byteland it is always the military officer's main worry to order his soldiers on parade correctly. In Bitland ordering soldiers is not really such a problem. If a platoon consists of n men, all of them have different rank (from 1 - lowest to n - highest) and on parade they should be lined up from left to right in increasing order of rank.

Sounds simple, doesn't it? Well, Msgt Johnny thought the same, until one day he was faced with a new command. He soon discovered that his elite commandos preferred to do the fighting, and leave the thinking to their superiors. So, when at the first rollcall the soldiers lined up in fairly random order it was not because of their lack of discipline, but simply because they couldn't work out how to form a line in correct order of ranks. Msgt Johnny was not at all amused, particularly as he soon found that none of the soldiers even remembered his own rank. Over the years of service every soldier had only learned which of the other soldiers were his superiors. But Msgt Johnny was not a man to give up easily when faced with a true military challenge. After a moment's thought a solution of brilliant simplicity struck him and he issued the following order: "men, starting from the left, one by one, do: (step forward; go left until there is no superior to the left of you; get back in line).". This did indeed get the men sorted in a few minutes. The problem was solved... for the time being.

The next day, the soldiers came in exactly the same order as the day before, and had to be rearranged using the same method. History repeated. After some weeks, Msgt Johnny managed to force each of his soldiers to remember how many men he passed when going left, and thus make the sorting process even faster.

If you know how many positions each man has to walk to the left, can you try to find out what order of ranks the soldiers initially line up in?

Input

The first line of input contains an integer t<=50, the number of test cases. It is followed by t test cases, each consisting of 2 lines. The first line contains a single integer n (1<=n<=200000). The second line contains n space separated integers wi, denoting how far the i-th soldier in line must walk to the left when applying Msgt Johnny's algorithm.

Output

For each test case, output a single line consisting of n space separated integers - the ranks of the soldiers, given from left to right in their initial arrangement.

Example

Input:
2
3
0 1 0
5
0 1 2 0 1

Output:
2 1 3
3 2 1 5 4
Warning: large Input/Output data, be careful with certain languages


点击打开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)


代码:

#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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值