spoj227. Ordering the Soldiers

SPOJ Problem Set (classical)

227. Ordering the Soldiers

Problem code: ORDERS

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

纠结到死的题。。。
理解了好长时间,才晓得怎么做
a数组来表示每个士兵左移的次数
则a[i]的含义可以理解为:i左边比i等级低的士兵个数
则知道前i个士兵拥有的等级集合,取第a[i]大(等级越大,表示越低),就是i士兵的等级。
而要确定对应每个i的这个集合,就成了本题的关键。
用s[i]表示前i个士兵等级的集合
则s[i]比s[i-1]多了一个i士兵的排名,而s[n]是确定的,s[n]={1,2,3,...,n}
这就启发我们倒着来解决每个士兵的排名


举个例子:
n = 5
a[ n ] = { 0, 1, 2, 0, 1 };
对于第5个士兵,s[ 5 ] = { 1, 2, 3, 4, 5 };
而与它对应的a[ 4 ] = 1,也就是说在他左边的士兵里,有1个比他小,他在他左边的士兵里排第四,故s[5]里第四小的排名就是他的,对应4
对于第4个士兵,s[ 4 ] = { 1, 2, 3, 5 };没有4表示第四小的排名已经被占
a[ 3 ] = 0, 也就是说他左边的士兵都比他等级高,他的排名就最大,故s[4]里最大的事5,就是他的排名
对于第3个士兵,s[ 3 ] = { 1, 2, 3 };
a[ 2 ] = 2, 表示第 3 个士兵左边的两个士兵等级都比他低,故取s[3]里最小的--1
对于第2个士兵,s[ 2 ] = { 2, 3 };
a[ 1 ] = 1, 表示第 2 个士兵左边的一个士兵等级比他低,故取s[2]里最小的--2
对于第1个士兵,s[ 1 ] = { 3 };
显然第一个士兵排名为3


故解题步骤为:


将1,2,3,...,n添加到集合s里
倒着处理每个士兵:
    1.找到s里第a[i]大的数k,赋值给r[i],为i士兵的排名
    2.将s里的k剔除


支持找第k大和加入、删除单个元素的操作的数据结构有:平衡树、线段树、树状数组等
树状数组实现起来比较简单,下面就是树状数组解法:

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 200010
using namespace std;

int c[N],a[N],p[N],n,l;
void modify(int x,int y)
{
    while(x<=n)
    {
        c[x] += y;
        x += x&-x;
    }
}
int sum(int x)
{
    int ans = c[ x ];
    while(x&=x-1)ans+=c[x];
    return ans;
}
int find(int k)     //找第k小
{
    int cnt=0,ans=0;
    for(int i=l; i>=0; i--)
    {
        ans+=(1<<i);
        if(ans>=n || cnt+c[ans]>=k)ans-=(1<<i);
        else cnt+=c[ans];
    }
    return ans+1;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        memset(c,0,sizeof(c));
        for(int i = 0 ; i < n ; i++ )
        {
            scanf("%d",a+i);
            modify( i+1, 1 );
        }
        //
        //这一段是求出n的二进制位数,放入l里
        l = 0;
        int w = n;
        while(w)
        {
            w>>=1;
            l++;
        }
        /
        for(int i = n - 1 ; i >= 0 ; i-- )
        {
            p[ i ] = find( i - a[ i ] + 1 );  //第a[i]+1大即为第i-a[i]+1小
            modify( p[ i ], -1);
        }
        for(int i = 0 ; i < n ; i++ )
        {
            printf("%d",p[i]);
            if(i==n-1)putchar('\n');
            else putchar(' ');
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值