题目:ZYB有一个排列PP,但他只记得PP中每个前缀区间的逆序对数,现在他要求你还原这个排列. 输入:
1
3
0 1 2
输出:
3 1 2
题解:num[k]-num[k-1] 是排列中第k个数贡献的逆序数对,
故 k-(num[k]-num[k-1])是第k个数在前k个数中的排名(从小到大)。
逆向确认排列,确认第N个数后,删除这个数的值。
再确认第N-1个数,它在剩下的数中排名 N-1-(num[N-1]-num[N-2])。依此类推。
很容易用线段树维护区间剩下数的个数,进而求得第K大。#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <algorithm>
using namespace std;
const int maxn=200005;
const int INF=1000000007;
struct Node{
int left,right;
int value;
}node[maxn<<2];
int num[maxn];
int ans[maxn];
void build(int i,int L,int R){
node[i].left=L;
node[i].right=R;
if(L==R){
node[i].value=1; //表示存在
return;
}
int mid=L+((R-L)>>1);
build(i<<1,L,mid);
build(i<<1|1,mid+1,R);
node[i].value=node[i<<1].value+node[i<<1|1].value;
}
int query(int i,int Key){ // 查找第Key大数
//必须查到叶子节点
if(node[i].left==node[i].right&&node[i].value==Key)
return node[i].right;
// 如果左边至少包含Key个数,查左子树的第Key大数
else if(node[i<<1].value>=Key)
return query(i<<1,Key);
//如果左边不够Key个数,查右子树的第Key-node[i<<1].value大
else return query(i<<1|1,Key-node[i<<1].value);
}
//这里的更新相当于删除确定的数
void update_line(int i,int L,int R,int change){
if(node[i].left>=L&&node[i].right<=R){
node[i].value=change;
return ;
}
int p1,p2;
int mid=(node[i].left+node[i].right)>>1;
if(L>mid)
update_line(i<<1|1,L,R,change);
else if(R<(mid+1))
update_line(i<<1,L,R,change);
else{
update_line(i<<1|1,L,R,change);
update_line(i<<1,L,R,change);
}
node[i].value=node[i<<1].value+node[i<<1|1].value;
}
int main()
{
int T,N,k;
int a,b,c;
scanf("%d",&T);
while(T--){
scanf("%d",&N);
build(1,1,N);
for(int i=1;i<=N;i++){
scanf("%d",&num[i]);
}
num[0]=0;
for(int i=N;i>=1;i--){
int cnt=i-(num[i]-num[i-1]);
ans[i]=query(1,cnt); // 询问剩余数中的第cnt大数
update_line(1,ans[i],ans[i],0); //相当于删除已经确认的数
}
for(int i=1;i<N;i++)
printf("%d ",ans[i]);
printf("%d\n",ans[N]);
}
}