题意
给你一组从1到n的排列,表示的是你当前所拥有的逆序对数,现在让你重新还原这个排列。
思路
我们知道
[1⋅⋅⋅i]
[
1
·
·
·
i
]
区间和
[1⋅⋅⋅⋅i−1]
[
1
·
·
·
·
i
−
1
]
区间的逆序对数了,那我们其实就可以确定第i个位置的大小是多少了,因为他们的差(设为p)其实就表明当前位置
i
i
<script type="math/tex" id="MathJax-Element-344">i</script>的这个数在他的前面有p个比他大,那么换句话说他其实就是他在当前区间是第p+1大的数字,那么就变成简单的线段树维护当前区间有几个数字这种问题了,具体怎么做,就是我们维护区间和,表示的是当前区间有多少个数字,每次查找区间第几大的时候我们先看他的右子树,优先往右子树走,如果当前要找第k大,如果右子树区间有k个那么就走右子树,小于k个,那么就取查左子树的(k-右子树现有的区间个数),之后找到之后把这个值变成0,pushup一下就好了
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e4+10;
int a[maxn],A;
struct seg
{
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int tree[maxn<<2];
void pushup(int rt)
{
tree[rt] = tree[rt<<1] + tree[rt<<1|1];
}
void build(int l,int r,int rt)
{
if(l == r)
{
tree[rt] = r - l + 1;
return ;
}
int m = (l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
void update(int pos,int l, int r,int rt)
{
if(l == r)
{
A = l;
tree[rt] = 0;
return ;
}
int m = (l+r)>>1;
if(tree[rt<<1] >= pos) update(pos,lson);
else update(pos-tree[rt<<1],rson);
pushup(rt);
}
};
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
seg tree;
vector<int>ans;
int n;
scanf("%d",&n);
tree.build(1,n,1);
for(int i = 0 ; i < n ; i++) scanf("%d",&a[i]);
int cnt = 0;
for(int i = n - 2 ; i >= 0 ; i --)
{
int p = a[i+1] - a[i];
tree.update(n-p-cnt,1,n,1); // 我这里写的是求第k大其实就是求第n-k小之后优先查左子树就好了
cnt++;
ans.push_back(A);
}
tree.update(1,1,n,1);
ans.push_back(A);
for(int i = ans.size()-1; i >=0 ; i--)
{
printf("%d%c",ans[i]," \n"[i == 0]);
}
}
}
/*
100
6
0 1 2 5 7 8
*/