题目链接: 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;
}
注:原创文章,转载请注明出处