POJ 2886 Who Gets the Most Candies? 线段树

http://poj.org/problem?id=2886

题意:

N个人玩一个游戏,每个人都有一个weight ,游戏从第K个人开始,第一轮标号为K的人退出游戏,下一个退出的人的编号由上一轮退出的人的weight决定,如果上一轮退出的人的weight

为正,则下一个退出的人是上一轮退出的人的左手边的第weight个人,如果是负数则是右手边

的第weight个人,每个人退出时候都有一个得分,得分是退出的编号的约数的个数,问得分最

高的人能得几分。 N <= 500.000

思路:

分析题目可以看出, 首先题目要求的是得分最高的人的得分,因此我们可以先预处理出所有编

号的得分,然后求出最大的得分,这样问题就变成求一个指定退出编号的人了。然后对于每个

人有一个weight, 现在我们要做的就是如何快速地从一个人求到另外一个人,这样我们可以利

用线段树来求,在N <= 500 000 的情况下, 这个可以在O( nlogn )的算法内求出。 

代码:

#include <stdio.h>
#include <string.h>
#define LL(a) ( (a)<<1 )
#define RR(a) ( (a)<<1|1 )

int N , K ;
const int MAXN = 500010 ;
char name[MAXN][11] ;
int val[MAXN] ;
int d_num[MAXN] ;
int _max  , pos ;
int num[MAXN<<2] ;

void cal(){
    for(int i=1;i<MAXN;i++)
        d_num[i] = 1 ;
    for(int i=2;i<MAXN;i++){
        for(int j=1;j*i<MAXN;j++){
            d_num[ i*j ] ++ ;
        }
    }
}
void build(int l , int r , int idx ){
    int mid = (l + r) >> 1 ;
    if(l == r){
        num[idx] = 1 ;
        return ;
    }
    build(l , mid , LL(idx) ) ;
    build(mid+1, r , RR(idx) ) ;
    num[idx] = num[ LL(idx) ] + num[ RR(idx) ] ;
}
int query(int l ,int r, int idx , int a , int b){
    if(l==a && r==b){
        return num[idx] ;
    }
    int mid = (l + r) >> 1;
    if(b <= mid){
        return query( l, mid, LL(idx) , a, b);
    }
    else if( mid < a ){
        return query( mid+1 , r ,RR(idx) , a ,b );
    }
    else{
        return query( l ,mid ,LL(idx) , a , mid ) + query( mid+1, r, RR(idx) , mid+1, b );
    }
}
void update1(int l,int r , int idx ,int pos){
    if(l == r){
        num[idx] -- ;
        return  ;
    }
    int mid = (l + r) >> 1;
    if( pos <= mid ){
        update1(l , mid , LL(idx) , pos );
    }
    else{
        update1(mid+1, r,  RR(idx) , pos );
    }
    num[idx] = num[ LL(idx) ] + num[ RR(idx) ] ;
}
int update(int l ,int r , int idx , int vv){
    if(l == r){
        return l ;
    }
    int mid = (l + r ) >> 1 ;
    if( num[ LL(idx) ] >= vv ){
        return update(l ,mid , LL(idx) , vv);
    }
    else{
        return update(mid+1, r , RR(idx) , vv-num[ LL(idx) ] ) ;
    }
}

void solve(){
    int nn = 1 ;
    int now = K ;

    while( nn <= N ){
        if(nn == pos){
            printf("%s %d\n",name[now] , d_num[pos] );
            return ;
        }
        int vv = val[now] ;
        int left = N - nn ;
        int n1 , n2 ;
        if( vv > 0 ){
            // go left
            n1 = query(1, N , 1 , 1 , now );
            update1(1 , N ,1 , now);
            n1 -- ;
            n2 = left - n1 ;
            vv %= left ;
            if( vv == 0 ){
                if(n1 == 0)
                    now = update(1 , N ,1 , left) ;
                else
                    now = update(1, N ,1 , n1 ) ;
            }
            else{
                if( vv <= n2 ){
                    vv = n1 + vv ;
                }
                else{
                    vv -= n2 ;
                }
                now = update(1, N , 1 , vv );
            }
        }
        else{
            // go right ;
            n1 = query(1 , N , 1 , 1 , now ) ;
            update1(1 , N ,1 , now);
            n1 --  ;
            n2 = left - n1 ;
            vv = -vv ;
            vv %= left ;
            if( vv == 0 ){
                if( n2 == 0 ){
                    now = update(1 , N ,1 , 1 );
                }
                else{
                    now = update(1 , N , 1 , n1 + 1 );
                }
            }
            else{
                if( vv <= n1 ){
                    now = update(1, N ,1 ,n1+1-vv);
                }
                else{
                    now = update(1 , N ,1 , left-(vv-n1)+1 );
                }
            }
        }
        nn++ ;
    }
}

int main(){
    cal() ;
    //freopen("1out.txt","w",stdout) ;
    while( scanf("%d %d",&N,&K) == 2){
        for(int i=1;i<=N;i++){
            scanf("%s %d",name[i] , val+i) ;
        }
        _max = -1 ;
        for(int i=1;i<=N;i++){
            if( d_num[i] > _max ){
                _max = d_num[i] ;
                pos = i ;
            }
        }
        build( 1, N ,1 );
        solve() ;
    }
    return 0 ;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值