POJ 2828 Buy Tickets(线段树)

点我看题目 

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

 

思路 :首先处理数据,不要从前往后处理,从后往前处理,因为后边的人是会影响前边的人的位置的。用线段树的每个结点记录这个区间中的空位置数,每次插入的时候将这个人放在第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

 

你可能感兴趣的:(poj)