题意
有
n
n
个人,每个人有一个权值,初始队列没有人,这 个人进行
n
n
次插队,每次插队代表一个权值为 的人插入第
x
x
个位置后。最后从前往后输出每个人的权值。
思路
虽然一个人现在插在
x
x
位置后,但是它最终的位置不一定是 ,再后来人的插入后可能位置还会变。但是,最后一个人插在
x
x
位置,这个人最后就是在 位置上;倒数第二个人,只用将最后一个人的位置“留空”,再找第
x
x
个没有“留空”的位置插入即可。不难形成算法,从后向前完成操作,建立一个初值均为 的数组。每次插入找第
x+1
x
+
1
个
1
1
将其赋为 ,再把队伍的对应位置附上这个人的权值。复杂度
O(n2)
O
(
n
2
)
。
但是找第
x
x
个 ,就是找前缀和为
x
x
<script type="math/tex" id="MathJax-Element-1309">x</script> 第一个位置,那么前缀和用树状数组维护修改,第一个位置二分找就行了。
代码
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define lowbit(x) ((x)&-(x))
#define N 200003
typedef long long LL;
using namespace std;
int a[N],v[N],q[N],c[N],n;
int query(int k)
{
int res=0;
while(k)
{
res+=c[k];
k^=lowbit(k);
}
return res;
}
void update(int k,int val)
{
while(k<=n)
{
c[k]+=val;
k+=lowbit(k);
}
return;
}
int find_pos(int k)
{
int L=1,R=n;
while(L<R)
{
int mid=L+R>>1;
if(query(mid)>=k)
R=mid;
else L=mid+1;
}
return L;
}
int main()
{
while(~scanf("%d",&n))
{
memset(c,0,sizeof(c));
FOR(i,1,n)scanf("%d%d",&a[i],&v[i]);
FOR(i,1,n)update(i,1);
DOR(i,n,1)
{
int t=find_pos(a[i]+1);
q[t]=v[i];
update(t,-1);
}
FOR(i,1,n-1)printf("%d ",q[i]);
printf("%d\n",q[n]);
}
return 0;
}