Who Gets the Most Candies?----POJ_2886----线段树之单点更新

题目地址:http://poj.org/problem?id=2886

/*
Author:Bob Lee
2012.9.26
=====================================================================
题目:N 个小孩围成一圈,他们被顺时针编号为 1 到 N。
每个小孩手中有一个卡片,上面有一个非 0 的数字,
游戏从第 K 个小孩开始,他告诉其他小孩他卡片上的数字并离开这个圈,
他卡片上的数字 A 表明了下一个离开的小孩,
如果 A 是大于 0 的,则下个离开的是左手边第 A 个,
如果是小于 0 的,则是右手边的第 -A 个小孩。
游戏将直到所有小孩都离开,在游戏中,
第 p 个离开的小孩将得到 F(p) 个糖果,F(p) 是 p 的约数的个数,
问谁将得到最多的糖果。输出最幸运的小孩的名字和他可以得到的糖果。
=====================================================================
思路:
反素数:
对 x来说约束个数 G(x),如果 对于 i<x 有 G(i)<G(x)
则称x为反素数。
那么这个题目里面就是要求出最大的反素数
这个是可以打表的,但是我们不知道到底是谁在这个数出去的
我们就需要用到模拟
另外,我们要引进一个初始值0
第0个孩子的num也为0
方便我们后面的操作
对于num>0,是它后面的,由于它本身也要已经是要删掉了的,所以
k = k+num-1
else
k = k+num
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
using namespace std;

#define maxn 500005
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1


int sum[maxn<<2];
int lastpos;

int RPrime[]={//反素数
    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,
    554400
};

int fact[]={//反素数约数个数
    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,216
};

struct c
{
    char name[20];
    int num;
}child[maxn];

void PushUp(int rt)
{
    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}

void build(int l,int r,int rt)
{
    if(l == r)
    {
        sum[rt] = 1;
        return;
    }
    int m = (l+r)>>1;
    build(lson);
    build(rson);
    PushUp(rt);
}

void update(int p,int l,int r,int rt)
{
    sum[rt]--;
    if(l == r)
    {
        lastpos = l;
        return;
    }
    int m = (l+r)>>1;
    if(p <= sum[rt<<1])
        update(p,lson);
    else
        update(p-sum[rt<<1],rson);
}

int main()
{
    int n,k;
    while(~scanf("%d%d",&n,&k))
    {
        //PS:用scanf和printf要快得多,不然超时
        for(int i=1;i<=n;i++)
        {
            scanf("%s%d",child[i].name,&child[i].num);
        }
        build(1,n,1);
        int P=0;

        //找出<=n的最大反素数
        for(int i=0;RPrime[i] <= n;i++)
        {
            P = i;
        }
        int low=1;
        int high = RPrime[P];
        lastpos = 0;
        child[lastpos].num = 0;

        /*
        我们已经知道<=n的最大反素数high
        现在我们就是要模拟到底谁是第high个出去的
        每次出去一个
        用线段树来保存这个区间还有多少人
        */

        while(low++<=high)
        {
            int mod = sum[1];
            //下面这两个k的循环公式,是自己在草稿纸上演算一下就可以推出来
            if(child[lastpos].num > 0)
                k = ((k+child[lastpos].num-2)%mod+mod)%mod + 1;
            else
                k = ((k+child[lastpos].num-1)%mod+mod)%mod + 1;
            update(k,1,n,1);
        }
        printf("%s %d",child[lastpos].name,fact[P]);
    }
}








 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值