POJ 2886 Who Gets the Most Candies?(线段树+反素数)

Who Gets the Most Candies?
Time Limit: 5000MS Memory Limit: 131072K
Total Submissions: 15617 Accepted: 4944
Case Time Limit: 2000MS

Description

N children are sitting in a circle to play a game.

The children are numbered from 1 to N in clockwise order. Each of them has a card with a non-zero integer on it in his/her hand. The game starts from the K-th child, who tells all the others the integer on his card and jumps out of the circle. The integer on his card tells the next child to jump out. Let A denote the integer. If A is positive, the next child will be the A-th child to the left. If A is negative, the next child will be the (A)-th child to the right.

The game lasts until all children have jumped out of the circle. During the game, the p-th child jumping out will get F(p) candies where F(p) is the number of positive integers that perfectly divide p. Who gets the most candies?

Input

There are several test cases in the input. Each test case starts with two integers N (0 < N ≤ 500,000) and K (1 ≤ K ≤ N) on the first line. The next N lines contains the names of the children (consisting of at most 10 letters) and the integers (non-zero with magnitudes within 108) on their cards in increasing order of the children’s numbers, a name and an integer separated by a single space in a line with no leading or trailing spaces.

Output

Output one line for each test case containing the name of the luckiest child and the number of candies he/she gets. If ties occur, always choose the child who jumps out of the circle first.

Sample Input

4 2
Tom 2
Jack 4
Mary -1
Sam 1

Sample Output

Sam 3

Source

POJ Monthly--2006.07.30, Sempr

题意:有n个熊孩子按顺时针的顺序围成一圈坐着,每个熊孩子手中都有一张写着一个数字的卡片。首先第k个熊孩子跳出圈,并且给其他熊孩子看他手中卡片上的数字是多少,如果数字是正数A那么在他左手边第A个熊孩子就是下一个要跳出圈的,反之如果是负数-A那么就是右手边第A个熊孩子跳出圈。如此往复,直到所有的熊孩子都出圈了游戏则结束了。每一个出圈的熊孩子都会得到糖果的奖励,得到糖果的个数为F(p),p的值代表的是第几个跳出圈,F(p)表示的是p的约数的个数。问哪个熊孩子得到的糖果最多。

思路:我们要找的那个熊孩子为F(p)最大,也就是要找出1~n中约数最多的那个数。这里就要用到反素数了,什么是反素数呢?令G(x)等于x约数的个数,那么对于任意一个小于x的数i都满足G(i)<G(x)的话,x即为反素数。所以我们要找的也就是1~n中最大的反素数,但是光找出最大反素数还不够。因为虽然我们知道是第几个跳出圈的熊孩子能得到最多的糖果,但是咱们并不知道他的名字呀。

所以这里就要用线段树来模拟这个游戏的过程了,建一颗线段树来表示区间内还有几个熊孩子。每一次更新操作都根据游戏规则让一个熊孩子出圈,也就是将一个叶子节点置为0,这样其他表示区间内有几个熊孩子的值也会相应变化。在更新节点的时候,如果左子树剩余的熊孩子比我们要找的熊孩子的序号大,直接在左子树中找,如果不够的话,那么在右子树中找的话就得把序号减去左子树中熊孩子的个数,这样才能正确找到要出圈的熊孩子。

另外还有一点要注意的是,因为每一次熊孩子都得出圈,那么如果熊孩子手中卡片上的值m为正数的话,后面熊孩子的相对位置就都得减一了。例如刚开始五个熊孩子,第一个出圈的是第三个孩子,随后第四个孩子就变成了第三个,第五个就变成了第四个,都依次往前推了一个。所以下一个要出圈的孩子的序号应该要在原来的基础上再减一,也就是k+m-1。如果m是负数的话就不需要减一,也就是k+m。

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=500050;
struct node
{
    int l,r;
    int num;
}segTree[4*N];

struct child
{
    char name[15];
    int val;
}c[N];
void build(int root,int l,int r)
{
    segTree[root].l=l;
    segTree[root].r=r;
    if(l==r)
    {
        segTree[root].num=1;
        return;
    }
    int mid=(l+r)>>1;
    build(root<<1,l,mid);
    build(root<<1|1,mid+1,r);
    segTree[root].num=segTree[root<<1].num+segTree[root<<1|1].num;
}

int update(int root,int index)
{
    segTree[root].num--;
    if(segTree[root].l==segTree[root].r)
    {
        segTree[root].num=0;
        return segTree[root].r;
    }
    if(index<=segTree[root<<1].num)
        return update(root<<1,index);
    else
        return update(root<<1|1,index-segTree[root<<1].num);//左子树中剩余人数不够,那么就得在右子树中找到不够的那一部分,所以得减去左子树中的人数

}
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
};
int main()
{
    int n,k,i;
    while(scanf("%d %d",&n,&k)!=EOF)
    {
        int cnt=0;
        while(RPrime[cnt]<=n)
        {
            cnt++;
        }
        cnt--;
        for(i=1;i<=n;i++)
            scanf("%s %d",c[i].name,&c[i].val);
        int mod=n;
        build(1,1,n);
        int pos=0;
        c[pos].val=0;
        for(i=0;i<RPrime[cnt];i++)
        {
            if(c[pos].val>0)
                k=((k+c[pos].val-2)%mod+mod)%mod+1;
            else
                k=((k+c[pos].val-1)%mod+mod)%mod+1;
            pos=update(1,k);
            mod--;
        }
        printf("%s %d\n",c[pos].name,fact[cnt]);
    }
    return 0;
}

阅读更多
文章标签: 线段树
个人分类: 线段树
上一篇CSU 2031 Barareh on Fire(BFS)
下一篇关于逆元
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭