【HDU5592】【权值线段树】根据逆序对求原数组

ZYB's Premutation

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1285    Accepted Submission(s): 677


 

Problem Description

ZYB has a premutation P,but he only remeber the reverse log of each prefix of the premutation,now he ask you to 
restore the premutation.

Pair (i,j)(i<j) is considered as a reverse log if Ai>Aj is matched.

 

 

Input

In the first line there is the number of testcases T.

For each teatcase:

In the first line there is one number N.

In the next line there are N numbers Ai,describe the number of the reverse logs of each prefix,

The input is correct.

1≤T≤5,1≤N≤50000

 

 

Output

For each testcase,print the ans.

 

 

Sample Input

 

1 3 0 1 2

 

 

Sample Output

 

3 1 2

 

 

题目大意:

第一行,测试组数

第二行,n(该数组的范围是1~n,所以不存在离散化问题)

第三行,n个数,每个数字表示在该位置之前的逆序对的数量

比如a[5]=5,表示在前五个位置有5个逆序对,但并不是在第五个位置上存在5个逆序对!!!

以下是官方题解

权值线段树找一下第k个什么的很方便

#include<cstdio>
#include<iostream>
#include<cstring>
#define MAXN 50005
using namespace std;
struct forma
{
    int l,r,val,num;
} tree[MAXN*4];
int a[MAXN],ans[MAXN];
void build(int root,int l,int r)
{
    tree[root].l=l;
    tree[root].r=r;
    tree[root].num=r-l+1;
    if(l==r)
    {
        tree[root].val=l;
        return ;
    }
    int mid=(l+r)>>1;
    build(root<<1,l,mid);
    build(root<<1|1,mid+1,r);
}
int query(int root,int k)
{
    if(tree[root].l==tree[root].r)
        return tree[root].val;
    if(tree[root<<1].num>=k)
        return query(root<<1,k);
    else
        return query(root<<1|1,k-tree[root<<1].num);
}
void update(int root,int pos)
{
    tree[root].num--;
    if(tree[root].l==tree[root].r)
        return ;
    if(pos<=tree[root<<1].r)
        update(root<<1,pos);
    else
        update(root<<1|1,pos);
    return ;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d",&n);
        memset(a,0,sizeof(a));
        memset(ans,0,sizeof(ans));
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        build(1,1,n);
        for(int i=n; i>=1; i--)
        {
            ans[i]=query(1,i-(a[i]-a[i-1])); //从剩下的数中选择a[i]-a[i-1]+1大的
            update(1,ans[i]);
        }
        for(int i=1; i<n; i++)
            printf("%d ",ans[i]);
        printf("%d\n",ans[n]);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值