问题描述
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 51≤T≤5,1≤N≤500001 \leq N \leq 500001≤N≤50000
输出描述
TTT行每行NNN个整数表示答案的排列.
输入样例
1 3 0 1 2
输出样例
3 1 2
题解:
设fi是第i个前缀的逆序对数,pi是第i个位置上的数,则fi−fi−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':' ');
}
}