题意:
输入n,表示插队的人数(一开始没有有排队)0号位置为售票处
接下来n行分别输入一个pos,val,表示插队的位置和插队的人的标号。插队的位置只能在已排队的人的位置插队。
求最终的队伍的排列顺序。
思路分析:
每次插队后,都插在插队位置的后面,那么位置后面的人都需要后退一个空位,才能成功插入,显然这样维护起来十分麻烦。所以我们不如倒过来排,对于先排的人,他的位置便不会再改变,对于后排的人,如果已经有人排了,那么就只能寻找后面的空位。
那为了方便找到想排的位置已经被占的情况下,后面的空位,对此我们可以选择无视已经在队伍中的人,那么对于我们最终插入的位置x+1,那么前面一定就有x个空位
举个栗子:
对于插入顺序为:
3
0 1
0 3
1 2
原始的队伍中是没有人的,0 0 0
倒序插入 ,首先是1 2,插入后队伍为 0 2 0
然后是0 3 ,插入后 3 2 0
最后0 1,插入后为 3 2 1 (这里因为原来的位置已经有人了,那么我们无视掉人的话,只有位置3的前面有0个空位,所以插入位置3。
所以我们为线段树的每个子叶标记上空位1。更新时,若排入子叶后将其标记清除为0,表示无空位。对于每个子叶单点维护后,最终查询即可。
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<math.h>
#include<vector>
#include<queue>
#define INF 0x3f3f3f
#define ll long long
#define fre() freopen(".in", "r", stdin);freopen(".out", "w", stdout);
using namespace std;
const double eps=1e-6;
const int N=2e5+10;
const int mod=1e9+7;
int n;
int pos[N],val[N],ans[N];
struct tree{
int l,r,sum;
int mid(){
return l+r>>1;
}
}t[N<<2];
inline void update(int node){//更新空位数
t[node].sum=t[node<<1].sum+t[node<<1|1].sum;
}
void build(int node,int l,int r){
t[node].l=l;t[node].r=r;
if(l==r){
t[node].sum=1;//标记空位
return ;
}
int mid=t[node].mid();
build(node<<1,l,mid);
build(node<<1|1,mid+1,r);
update(node);
}
//单点修改
void change(int node,int x,int y){
if(t[node].l==t[node].r){
t[node].sum=0;//标记这个点被插队
ans[t[node].l]=y;//记录下该位置的人
return ;
}
if(x<=t[node<<1].sum) change(node<<1,x,y);//如果想插入的位置小于子节点的可插空数
else change(node<<1|1,x-t[node<<1].sum,y); //否则需要减去左结点能插空的人数,得到相对右结点的插空位置
update(node);
}
int main() {
while(~scanf("%d",&n)){
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;i++){
scanf("%d%d",&pos[i],&val[i]);
}
build(1,1,n);
// for(int i=1;i<=n<<1;i++)printf("t[%d] [%d,%d] sum=%d \n",i,t[i].l,t[i].r,t[i].sum);
for(int i=n;i>=1;i--){//因为后面插完队后不会被前面的影响,所以逆序改变
change(1,pos[i]+1,val[i]);
}
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
printf("\n");
}
return 0;
}