poj2886-Who Gets the Most Candies?(线段树)

3 篇文章 0 订阅

poj2886-Who Gets the Most Candies?(线段树)

题目链接:http://poj.org/problem?id=2886

又是一个圈,然后又删人...不过,删人有规则,有顺时针又有逆时针,然后问你n个删除的人中第i个被删除人的i约数个数最大值是多少.

数据范围达到50 0000 ,有点多了,所以,一般这样可以打表...

打一个反素数表,对于n个人来说,它的最大约数个数就等于表中<=n的书中最大的反素数m约数个数;

最后就是如何输出第m个人的名字,其实还是采用模拟法,为了减少时长,我们才采用了线段树

此处,线段树的random域依然是记录【i  ,  j】的空位个数

期间遇到Runtime Error —— 我们知道 i == j 是线段树结点,可是我在每进行这一判断的时候,竟然直接默认它为父节点,然后就出错了

虽然它折磨我许久,不过很开心,我学会独立思考random域如何运用了,也没看模板,自行思考解决

代码如下:

#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#define maxn 500050


struct node
{
    int l , r ;
    int leftnum ;
}a[maxn*4];
int leftsum ;/*the sum of left numbers*/
int next[maxn] ;
char name[maxn][15];
int maxperson[40] = {1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,
7560,10080,15120,20160,25200,27720,45360,50400,55440,83160,110880,166320,221760,
277200,332640,498960,500001};
int maxvalue[40] = {1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60,64,72,80,
84,90,96,100,108,120,128,144,160,168,180,192,200,1314521};

void init(int le , int ri , int i , int k )
{
    int mid;
    a[i].l = le ;
    a[i].r = ri ;
    a[i].leftnum = ri-le+1 ;
    if( le <= k && k <= ri ) a[i].leftnum -- ;
    if( a[i].l == a[i].r ) return;
    mid = ( a[i].l+a[i].r )/2 ;
    init( le , mid , 2*i , k) ;
    init( mid+1 , ri , 2*i+1 , k) ;
}
int getnum( int le , int re , int i )
{///专门用来得到对任一位置loc,1-loc-1 以及 loc+1 - n的空位个数
    int mid = ( a[i].l + a[i].r )/2 ;
    if( le > re ) return 0 ;
    ///一开始这步错了,其实范围符合就可以直接返回了
    if( a[i].l == le && re == a[i].r ) return a[i].leftnum ;
    if( le > mid )
    {
        return getnum( le , re , 2*i+1 );
    }
    else if( re <= mid )
    {
        return getnum ( le , re , 2*i );
    }
    else
    {
        return getnum(le , mid , 2*i) + getnum(mid+1 , re , 2*i+1) ;
    }
}
int getans( int insert , int i )
{///找到插入位,然后插入就好了
    int m ;
    int leftt = a[i].l ;
    int rightt = a[i].r ;
    int mid ;
    if( leftt == rightt )
    {
        a[i].leftnum = 0 ;

        return a[i].l ;
    }
    m = a[2*i].leftnum;///在使用2i 的时候,一定要保证left != right
    if( insert <= m )
    {
        a[i].leftnum -- ;
        return getans( insert , 2*i ) ;
    }
    else
    {
        a[i].leftnum -- ;
        return getans( insert - m , 2*i+1 ) ;
    }
}
int main()
{

    int i ; int p; int star ;
    int n , k ; int per ; int loc ;
    int lef ; int rig ;
    while( scanf( "%d%d", &n , &k ) != EOF )
    {
        leftsum = n ;
        for( i = 1; i <= n ; i ++ )
        {
            scanf( "%s%d", name[i] , &next[i] ) ;
        }
        for( p = 0 ; p < 40 ; p++ )
        {
            if( maxperson[p] > n )
             break ;
        }
        per = p-1;
        p = maxperson[per];
///一定要初始化
        init( 1 , n , 1 , k );
        leftsum -- ;

        loc = k ; k = next[k] ;
        if(leftsum)
        {
          for( i = 1 ; i < p ; i++ )
          {
            lef = getnum( 1 , loc - 1 , 1 );
            rig = leftsum - lef ;
            /// k 的情况还挺多,不过如果能化为一种就很好了,我把k的值全部转化为从1开始要插入的第k个空位
            if( k > 0 )
            {
                k = k + lef ;
                k = k % leftsum == 0 ? leftsum : k%leftsum ;
            }
            else
            {
                k =  0 - k ;
                k = k + rig ;
                k = k % leftsum == 0 ? leftsum : k%leftsum ;
                k = leftsum - k + 1 ;
            }
            loc = getans( k , 1 ) ;
            leftsum -- ;
            if(!leftsum) break;
            k = next[loc] ;
          }
        }

        printf( "%s %d\n" , name[loc] , maxvalue[per] ) ;
    }


    return 0;
}

       
       
      
      
     
     
    
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值