HDU 5592 ZYB's Premutation 线段树(查找动态区间第K大)

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


一对数(i,j)当(i<j)时被认为是逆序对。
​​


输入描述
第一行一个整数T表示数据组数。


接下来每组数据:


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


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


数据保证合法.


1≤T≤51 \leq T \leq 51≤T≤5,1≤N≤500001 \leq N \leq 500001≤N≤50000


输出描述
T行每行N个整数表示答案的排列.


输入样例
1
3
0 1 2


输出样例
3 1 2

树的节点保存三个值:左端点a,右端点b,该区间剩下的数字的个数sum。
查询第k大的时候,当k大于右子节点的sum,则查询左子节点第(k-右子节点的sum)大,否则查询右子节点,当左端点等于右端点时返回。

#include <stdio.h>
#include <set>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

struct Tree
{
    int a,b,sum;
}t[200000];

void init(int x,int y,int num)
{
    t[num].a=x;
    t[num].b=y;
    if(x==y)
    {
        t[num].sum=1;
        return;
    }
    int mid=(x+y)/2;
    init(x,mid,num<<1);
    init(mid+1,y,(num<<1)+1);
    t[num].sum=t[num<<1].sum+t[(num<<1)+1].sum;
}

int query(int x,int num)
{
    t[num].sum--;
    if(t[num].a==t[num].b) return t[num].a;
    if(x>t[(num<<1)+1].sum) return query(x-t[(num<<1)+1].sum,(num<<1));
    else return query(x,(num<<1)+1);
}


int main()
{
    int n,T;
    int a[50005],b[50005];
    while(~scanf("%d",&T))
    {
        while(T--)
        {
            scanf("%d",&n);
            init(1,n,1);
            for(int i=0; i<n; i++)
            {
                scanf("%d",&a[i]);
            }
            //  for(set<int>::iterator it=se.begin();it!=se.end();it++) printf("%d ",*it);
            for(int i=n-1; i>0; i--)
            {
                int temp=a[i]-a[i-1];
                b[i]=query(temp+1,1);
            }
            b[0]=query(1,1);
            for(int i=0; i<n; i++)
                if(!i) printf("%d",b[i]);
                else printf(" %d",b[i]);
            printf("\n");
        }
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值