大概说一下题意:
有 个人,带着想要插队的位置(从 开始编号)和自身的一个 值进行插队,输出最后的 序列。
题意就是这样,正着想没什么思路那就想想逆推,毕竟正难则反。
其实这道题为什么要逆推也容易理解。序列始终是一个动态序列,我们无法在要求的时间内处理,而最后的序列是固定的,所以我们考虑逆推。
从后往前,那么每个人的位置其实都固定了。
比如样例:
为了方便处理,我们首先考虑将队伍的开头从 变为 ,那么输入就转为:
接下来我们模拟一遍逆推的过程:
整个队伍目前无人:
对于:
,需要的位置是
,补上去:
对于:
,需要的位置是
,从空位置中补上去(一定要是空位置):
对于:
,需要的位置是
,补上去:
对于:
,补上去:
得到答案。
那么应该如何实现这个模拟呢。
当然就是线段树了。
每个节点除了维护区间端点之外再维护一个 值,表示该区间内还有 个空位置。
参考代码:
#include <cstdio>
const int Max=2e5+5;
struct Node{
int X,Y,Num;
}Tree[Max<<3];
int N,Loc[Max],Val[Max],Ans[Max];
void MakeTree(int P,int X,int Y){
Tree[P].X=X;Tree[P].Y=Y;
if(X==Y){
Tree[P].Num=1;
} else if (X<Y){
MakeTree(P<<1,X,X+Y>>1);
MakeTree(P<<1|1,(X+Y>>1)+1,Y);
Tree[P].Num=Tree[P<<1].Num+Tree[P<<1|1].Num;
}
}
void Insert(int P,int X,int Y){
if(Tree[P].X==Tree[P].Y){
Tree[P].Num=0;Ans[Tree[P].X]=Y;return;
}
if(Tree[P<<1].Num>=X){
Insert(P<<1,X,Y);
} else {
Insert(P<<1|1,X-Tree[P<<1].Num,Y);
}
Tree[P].Num=Tree[P<<1].Num+Tree[P<<1|1].Num;
}
int main(){
int I,J,K;
while(~scanf("%d",&N)){
MakeTree(1,1,N);
for(I=1;I<=N;I++){
scanf("%d%d",&Loc[I],&Val[I]);Loc[I]++;
}
for(I=N;I>=1;I--){
Insert(1,Loc[I],Val[I]);
}
for(I=1;I<=N;I++){
printf("%d ",Ans[I]);
}
putchar('\n');
}
return 0;
}