BC的描述:
问题描述
问题描述
ZYBZYBZYB有一个排列PPP,但他只记得PPP中每个前缀区间的逆序对数,现在他要求你还原这个排列. (i,j)(i<j)被称为一对逆序对当且仅当Ai>Aj
输入描述
第一行一个整数TTT表示数据组数。 接下来每组数据: 第一行一个正整数NNN,描述排列的长度. 第二行NNN个正整数Ai,描述前缀区间[1,i]的逆序对数. 数据保证合法. 1≤T≤51,1≤N≤500001.
输出描述
TTT行每行NNN个整数表示答案的排列.
输入样例
1 3 0 1 2
输出样例
3 1 2
假设原数组为a数组,给你的是b数组,b[i]代表在a数组中从第一个数到第i个数的逆序对的个数,则s = b[i] - b[i-1]即为在a数组中第i个数与前面的数构成逆序对的个数,也就是说前i-1个数中有s个数比第i个数大,则a[i]即为数组中第s+1大的数;
所以需要先预处理一下b数组,从n开始向1循环 b[n] -= b[n-1];
将处理过的b数组,从n开始循环,此时,b[i]代表在a数组中前面有b[i]个数比a[i]大,所以只需要找第k大数就可以了;
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
const int N = 5e4+10;
struct SegmentTree
{
int l, r, x, sum;
};
SegmentTree Tree[4*N];
int n, m;
int p[N],f[N];
void UpdateUp(int k)
{
Tree[k].sum = Tree[k<<1].sum + Tree[k<<1|1].sum;
}
void build(int l, int r, int k)
{
Tree[k].l = l, Tree[k].r = r;
if(l==r)
{
Tree[k].x = m --, Tree[k].sum = 1;
return ;
}
int mid = (l+r) >> 1;
build(l,mid,k<<1);
build(mid+1,r,k<<1|1);
UpdateUp(k);
}
void query(int k, int n,int &s)
{
if( Tree[k].l == Tree[k].r && n==1)
{
Tree[k].sum = 0;
s = Tree[k].x;
return ;
}
if(n <= Tree[k<<1].sum)
query(k<<1, n, s);
else
query(k<<1|1 , n - Tree[k<<1].sum, s);
UpdateUp(k);
}
void test()
{
scanf("%d",&n);
m = n;
build(1,n,1);
long long sum = 0;
for(int i=1; i<=n; i++)
{
scanf("%d",&p[i]);
p[i] -= sum, sum += p[i];
}
for(int i=n; i>=1; i--)
query(1, p[i]+1, f[i]);
for(int i=1; i<=n; i++)
printf(i==n?"%d\n":"%d ",f[i]);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
test();
return 0;
}