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

来源:互联网 发布:驱蚊草 知乎 编辑:程序博客网 时间:2024/05/29 19:30

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

/*Author:Bob Lee2012.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-1elsek = 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|1int 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]);    }}