POJ 2828 Buy Tickets ( 线段树—单点更新 )

题意:很多人(1~200000)在后半夜还在排队,由于天太黑,人们即使插队也不会被人发现,每个人的名字由一个整数代表([0, 32767] )。现在一次给出每个人插入的位置,(0代表第一个,1代表第二个。。。),计算N个人插入队后,队中各个位置都是谁。 

分析:
正向思考的话,这道题就是最简单的模拟了,传统的双层O(n*n),达到了4*10^10 , 计算机每秒处理复杂度的效率在10^8左右,呃。。 4S必然不够。。。 - - 

每个节点除了左右区间外,再存储一下当前节点区间剩余的位置数量,那么一个人的位置表示的是他前面的人的数量,而先进队的人往往会被后来者挤到后面去,所以我们从后往前考虑,这样只要去找到那个包含该数量的区间,然后把这人放进去就可以了。

//8684K	1485MS
#include <stdio.h>
#define MAX  200010
#define MID(X,Y) (((X)+(Y))>>1)
#define L(X) (X<<1)
#define R(X) ((X<<1)|1)

struct node
{
    int weight ;
    int left , right ;
}node[MAX<<2];

int index ;
int pos[MAX],val[MAX] ;
int ans[MAX] ;

void Build_Tree ( int const left , int const n , int const right )
{
    node[n].left = left ;
    node[n].right = right ;
    node[n].weight = right - left + 1 ;
    if ( left < right )
    {
        int mid ;
        mid = MID(left , right ) ;
        Build_Tree ( left , L(n) , mid ) ;
        Build_Tree (  mid + 1 , R(n) , right ) ;
    }
}

void Update ( int pre , int const n )
{
    node[n].weight -- ;
    if ( node[n].right == node[n].left )
    {
        index = node[n].left ;
        return ;
    }
    else
    {
        if ( pre <= node[L(n)].weight )
        {
            Update ( pre , L(n) ) ;
        }
        else
        {
            pre -= node[L(n)].weight ;
            Update ( pre , R(n)) ;
        }
    }
}


int
main ( )
{
    int n ;
    while ( EOF != scanf ("%d" , & n ) )
    {
        Build_Tree ( 1 , 1 , n ) ;
        int i ;
        for ( i = 1 ; i <= n ; i ++ )
        {
            scanf ("%d%d" , &pos[i] , &val[i] ) ;
        }
        for ( i = n ; i >= 1 ; i -- )
        {
            Update ( pos[i] + 1 , 1 ) ;
            ans[index] = val[i] ;
        }
        for ( i = 1 ; i <= n ; i ++ )
        {
            if ( i != n )
            {
                printf ("%d " , ans[i] ) ;
            }
            else
            {
                printf ("%d\n" , ans[i] ) ;
            }
        }
    }
    return 0 ;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值