题意 :说实话,看题的时候根本没怎么看懂,每个单词我倒是认识,但是拼一块儿我觉得就有点拗口了,一开始我以为的站右边是以为队伍如果画出来应该是上下的,谁知看了底下的那个样例解释才知道原来队伍时左右的,,,难怪说插到右边呢。。。。大概题意就是,队列中的每个人都被赋一个特定的值,给出所有插队的人及他们插队后他们所站的位置信息,然后输出队列中的人的最终的排列顺序。
思路 :首先处理数据,不要从前往后处理,从后往前处理,因为后边的人是会影响前边的人的位置的。用线段树的每个结点记录这个区间中的空位置数,每次插入的时候将这个人放在第pos[i]个空格的地方,因为后边的人如果排在前面人的前面,那么我们对前面的人进行操作的时候,那个位置就会被占了,前面人的位置就会向后移一格。这样就可以用线段树来处理了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> const int maxn = 200100 ; using namespace std; int pos[maxn],val[maxn],size[maxn*3],ans[maxn],point[maxn] ; //第i个人的属性值为val[i],插入第pos[i]+1个空格位置 //ans[i]是指排列中第i个位置的队员序号,在线段树中区间[k]的叶节点序号为point[k] //线段树中结点j所代表区间的空位数为size[j] int n ; void build(int l,int r,int x)//从结点x出发,构造区间(l,r)的线段树 { size[x] = r-l+1 ; // 存储结点x所代表区间空位置数 if(l == r)//若该区间仅一个元素,则设置该元素的叶节点序号 { point[l] = x ; return ; } int mid = (l+r)/2 ; build(l,mid,x+x) ; build(mid+1,r,x+x+1) ; } int countt(int sum,int l,int r,int x)//计算第sum个空位的叶节点序号 { if(l == r) return l ; int mid = (l+r)/2 ; if(size[x+x] >= sum)//若左子树的空位数不少于sum则递归左子树 return countt(sum,l,mid,x+x) ; return countt(sum-size[x+x],mid+1,r,x+x+1) ; } void get(int i)//从叶节点序号i出发向上调整所在字树的空位数 { while(i > 0) { size[i]-- ; i = i/2 ; } } void Init() { for(int i = 1 ; i <= n ; i++) scanf("%d %d",&pos[i],&val[i]) ; } void solve() { memset(size,0,sizeof(size)) ; build(1,n,1) ; for(int i = n ; i >= 1 ; i--) { int temp = countt(pos[i]+1,1,n,1) ; ans[temp] = i ; get(point[temp]) ; } for(int i = 1 ; i <= n-1 ; i++) printf("%d ",val[ans[i]]) ; printf("%d ",val[ans[n]]) ; } int main() { while(~scanf("%d ",&n)) { Init() ; solve() ; } return 0; }