hdu 5592 线段树

问题描述
ZYBZYBZYB有一个排列PPP,但他只记得PPP中每个前缀区间的逆序对数,现在他要求你还原这个排列.

(i,j)(i<j)(i,j)(i < j)(i,j)(i<j)被称为一对逆序对当且仅当Ai>AjA_i>A_jAi>Aj
输入描述
第一行一个整数TTT表示数据组数。

接下来每组数据:

第一行一个正整数NNN,描述排列的长度.

第二行NNN个正整数AiA_iAi,描述前缀区间[1,i][1,i][1,i]的逆序对数.

数据保证合法.

1≤T≤51 \leq T \leq 51T5,1≤N≤500001 \leq N \leq 500001N50000
输出描述
TTT行每行NNN个整数表示答案的排列.
输入样例
1
3
0 1 2
输出样例
3 1 2


题解:
设fi是第i个前缀的逆序对数,pi是第i个位置上的数,则f​i​​−f​i−1​​是iii前面比pi大的数的个数.我们考虑倒着做,当我们处理完i后面的数,
第i个数就是剩下的数中第f_i-f_{i-1}+1大的数,用线段树和树状数组可以轻松地求出当前第k个是1的位置,复杂度O(NlogN)

#include<cstdio>
#include<algorithm>
#include<string>
#include<iostream>
using namespace std;
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
const int maxn=50009;
int a[maxn<<2],ans[maxn<<2];
int sum[maxn<<2];
void pushup(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int rt,int l,int r)
{
    if(l==r){
        sum[rt]=1;
        return;
    }
    int m=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(rt);

}
int querry(int rt,int l,int r,int v)
{
    if(l==r){
        sum[rt]=0;
        return l;
    }
    int res;
    int m=(l+r)>>1;
    if(sum[rt<<1|1]>=v)res=querry(rson,v);
    else res=querry(lson,v-sum[rt<<1|1]);
    pushup(rt);
    return res;
}
int main()
{
    int t;
    cin>>t;
    while(t--){
        int n;
        scanf("%d",&n);
        a[0]=0;
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        build(1,1,n);
        for(int i=n;i>=1;i--){
            int f=a[i]-a[i-1];
            ans[i]=querry(1,1,n,f+1);
        }
        for(int i=1;i<=n;i++)
            printf("%d%c",ans[i],(i==n)?'\n':' ');
    }
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值