poj2828-Buy Tickets(线段树的经典运用)

poj2828-Buy Tickets 连接如下:http://poj.org/problem?id=2828

(线段树的经典运用)

题意就是给你一些数字和他们的插入位置,最后输出他们的顺序。看到这道题目,我也没反应过来这道题目和线段树有什么联系,关键是知道也不会用啊,线段树的random域到底用来保存什么好就很纠结了...
这道题目突破点在于,越后面插入的人他的位置越不可能变动,比如最后一个人,他插在哪就站在哪,它的序号不会发生变化。所以最好的办法就是从后往前推。
假设一共有n个人,对于倒数第i个人而言,假设他要插在第k个位置上,那么我只要从前往后数到第k个空位然后插入就好了。后面我已经处理的i-1个人的插入对于他而言没有影响。random域就可以用来保存该段区间的空位数。
核心代码在于:
    if( a[i].l == a[i].r )
    {
        que[a[i].l] = value ;
    }
    else
    if( a[2*i].num >= postn )
    {
如果前段区间的空位数比要插入的位置大
        renew( postn , value , 2*i ) ;
    }
    else
    {
寻找后段区间的插入
        renew( postn - a[2*i].num , value , 2*i+1 ) ;
    }
    a[i].num--;空位数递归 -1
这道题目运用的逆序思想很好的解决了这样的插入问题,以前我们只会用链表来解决插入,现在又有了一种新的思路!
最后的代码如下:
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#define maxn 200001
int que[maxn] , pos[maxn] , val[maxn] ;
int nn ;
struct point
{
    int l , r ;
    int num ;
}a[maxn*4];
void init(int le , int ri , int i)
{
    int mid ;
    a[i].l = le ;
    a[i].r = ri ;
    if( a[i].l == a[i].r )
    {
        a[i].num = 1 ;
        return ;
    }
    mid = ( a[i].l + a[i].r )/2 ;
    init( le , mid , 2*i ) ;
    init( mid+1 , ri , 2*i+1 ) ;
    a[i].num = a[2*i].num + a[2*i+1].num ;


}
int renew( int postn , int value  , int i )
{///core code - to find the people i's (whose value is "value") insert position(postn).
    int mid ;
    if( a[i].l == a[i].r )
    {
        que[a[i].l] = value ;
    }
    else
    if( a[2*i].num >= postn )
    {
        renew( postn , value , 2*i ) ;
    }
    else
    {
        renew( postn - a[2*i].num , value , 2*i+1 ) ;
    }
    a[i].num--;
    return 1 ;
}
int main()
{
    int i ;
    while( scanf ( "%d" , &nn ) != EOF )
    {
        for( i = 0 ; i < nn ; i++ )
        {
            scanf( "%d%d" , &pos[i] , &val[i] ) ;

        }init( 1 , nn , 1 );
        for( i = nn-1 ; i >= 0 ; i-- )
        {
            renew( pos[i]+1 , val[i] , 1 ) ;
        }

        for( i = 1 ; i < nn  ; i++ )
        {
            printf( "%d " , que[i] ) ;

        }printf( "%d\n" , que[i] ) ;
    }
    return 0;
}

      
      
     
     
    
    

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值