http://acm.csu.edu.cn:20080/csuoj/problemset/problem?pid=2222
Description
Wells有一个长度为N的数列Ai,这N个数互不相同
Wells已经计算完成了每个数对逆序对数量的贡献,也就是位置处于这个数之前但比这个数大的数的个数
现在Wells想请你通过这些信息复原原数列
Input
第一行包含一个正整数N(N<=100000)
第二行,N个正整数,为数列A中出现的数
第三行,N个整数,为前一行中对应的那个数对逆序对的贡献(Ai<=100000)
Output
一行,为复原后的数列
Sample Input
5 33 11 22 44 55 0 2 1 1 0
Sample Output
33 22 11 55 44
Hint
样例输出中11前33>11,22>11,所以11对逆序对的贡献为2
Source
题目大意:自己想了一个小时饶了无数弯路之后终于找到了正解。这题其实可以转化成求动态第k大的问题,我们来分析一波:先读入数列的值和对应的贡献,然后按照值从小到大排序,从小到大遍历该序列,因为第i个数的贡献为gxi,即i之前有gxi个数大于它,因此我们把它放到第gxi+1大的位置上即可。我们以样例为例:(11,2)、(22,1)、(33,0)、(44,1)、(55,0);此时可供选择的位置有:1、2、3、4、5;(1)因为11之前有2个大于它的元素而11又是当前最小的,因此要把11放到a[2+1]=a[3]=3的位置上,此时可供选择的位置有:1、2、4、5;(2)分析同上,要把22放到a[1+1]=a[2]=2的位置上,此时可供选择的位置有:1、4、5;(3)要把33放到a[0+1]=a[1]=1的位置上,此时可供选择的位置有:4、5;(4)要把44放到a[1+1]=a[2]=5的位置上,此时可供选择的位置有:4;(5)要把55放到a[0+1]=a[1]=4的位置上。现在清楚了吧~那么如何求动态第k大呢?就要用到权值树状数组了!还不知道这是什么玩意的同学请戳这里,初始我们对[1,n]的每一个i做add(i,1)操作,(add(i,1)即在树状数组中插入1个值为i的数)就得到了我们上面用到的位置序列,然后对于每个gxi,我们找到当前第gxi+1大的数,把vi放到该位置上同时把这个数从树状数组中删去即可。
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
struct node
{
int v,gx;
bool operator <(const node& x)const
{
return v<x.v;
}
};
node a[100005];
int tree[100005];
int ans[100005];
int n;
inline int lowbit(int x)
{
return x&(-x);
}
void add(int i,int v)
{
for(;i<=n;i+=lowbit(i))
tree[i]+=v;
}
int findk_th(int k)
{
int re=0,sum=0;
for(int i=20;i>=0;i--)
{
re+=(1<<i);
if(re>=n||sum+tree[re]>=k)
re-=(1<<i);
else
sum+=tree[re];
}
return re+1;
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&a[i].v);
for(int i=0;i<n;i++)
scanf("%d",&a[i].gx);
sort(a,a+n);
for(int i=1;i<=n;i++)
add(i,1);
int temp;
for(int i=0;i<n;i++)
{
temp=findk_th(a[i].gx+1);
ans[temp]=a[i].v;
add(temp,-1);
}
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
putchar('\n');
return 0;
}