点我看题目 

题意 :说实话,看题的时候根本没怎么看懂,每个单词我倒是认识,但是拼一块儿我觉得就有点拗口了,一开始我以为的站右边是以为队伍如果画出来应该是上下的,谁知看了底下的那个样例解释才知道原来队伍时左右的,,,难怪说插到右边呢。。。。大概题意就是,队列中的每个人都被赋一个特定的值,给出所有插队的人及他们插队后他们所站的位置信息,然后输出队列中的人的最终的排列顺序。

 

思路 :首先处理数据,不要从前往后处理,从后往前处理,因为后边的人是会影响前边的人的位置的。用线段树的每个结点记录这个区间中的空位置数,每次插入的时候将这个人放在第pos[i]个空格的地方,因为后边的人如果排在前面人的前面,那么我们对前面的人进行操作的时候,那个位置就会被占了,前面人的位置就会向后移一格。这样就可以用线段树来处理了。

#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\n",val[ans[n]]) ;
}
int main()
{
    while(~scanf("%d\n",&n))
    {
        Init() ;
        solve() ;
    }
    return 0;
}
View Code

 

posted on 2014-02-20 10:52  枫、  阅读(252)  评论(0编辑  收藏  举报