题目链接: http://poj.org/problem?id=2828
题目大意: 有N个人在排队买票,每个人可站的位置从0到N
后面来的人可能会插队,也有可能站在当前队伍的最后面
N行,每行两个数,pas表示刚来的这个人会站在当前队伍的第pas位置上
val表示这个人对应的值(0<=val<=i-1),最后按每个人所在的位置输出其相应的值
解题思路: 后面加入的人,必定会站在当前队伍之间或者最后面
因为后面加入的人一定会改变前面确定的顺序,所以从后往前来确定队伍的顺序,先看一组数据
0 2
1 1
1 38
0 31
初始化序列为 -1 -1 -1 -1 (-1表示这个位置没有被确定)
0 31,把值为31的人插入到剩余未确定位置中的第0个位置,得到序列 31 -1 -1 -1
1 38,把值为38的人插入到剩余未确定位置中的第1个位置,得到序列 31 -1 38 -1
以此类推最后的序列为 31 2 38 1
为什么每次加入的位置剩余空位的第val个呢?
因为我们是从后面往前的,后面加入的数不会影响比它之前的数所在的位置。
用线段树O(logN)找出左到右的第pas个位置(找出之后再往上更新),最下层的结点初始化值为1,其余的结点存储区间值的总和
代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define INF 0x3f3f3f3f #define MAX 200100 #define MID(a,b) (a+b)>>1 #define L(a) a<<1 #define R(a) (a<<1|1) typedef struct{ int left,right,num; }Node; Node Tree[MAX<<2]; int pas[MAX],val[MAX],ans[MAX],kk,n; void Build(int t,int l,int r) { Tree[t].left=l,Tree[t].right=r; if(Tree[t].left==Tree[t].right) { Tree[t].num=1; //初始化为1 return ; } int mid=MID(Tree[t].left,Tree[t].right); Build(L(t),l,mid); Build(R(t),mid+1,r); Tree[t].num=Tree[L(t)].num+Tree[R(t)].num; } void Query(int t,int l,int r,int m) { if(Tree[t].left==Tree[t].right) { kk=Tree[t].left; //找到结点,并且标记 Tree[t].num--; return ; } int mid=MID(Tree[t].left,Tree[t].right); if(Tree[L(t)].num>=m) //满足条件时优先选择左子树 { Query(L(t),l,mid,m); } else //否则选择右子树 { Query(R(t),mid+1,r,m-Tree[L(t)].num); } Tree[t].num=Tree[L(t)].num+Tree[R(t)].num; //更新 } int main() { int i,j,k,m,temp; while(scanf("%d",&n)!=EOF) { memset(Tree,0,sizeof(Tree)); Build(1,1,n); for(i=1;i<=n;i++) scanf("%d%d",&pas[i],&val[i]); for(i=n;i>=1;i--) { Query(1,1,n,pas[i]+1); //查找剩余空位中的第pas[i]个位置所在的点 ans[kk]=val[i]; //标记点 } for(i=1;i<=n;i++) printf("%d%c",ans[i],i==n?'\n':' '); } return 0; }
注:原创文章,转载请注明出处