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

点我看题目

题意 :n个小盆友从1到n编号按顺时针编号,然后从第k个开始出圈,他出去之后如果他手里的牌是x,如果x是正数,那下一个出圈的左手第x个,如果x是负数,那出圈的是右手第-x个,游戏中第p个离开的孩子得到的糖果是f(p)个,f(p)是p的约数的个数。

思路 : 因为f(p)是p的约数的个数,所以,要用到反素数,反素数的定义及求法反素数s的约数个数比小于它的数的约数个数都多,最大反素数就是要找到的那个得到糖果最多的人。线段树记录的是还剩多少 人未出列。

#include <iostream>

#include <stdio.h>

#include <string.h>



using namespace std;



const int maxn = 506000 ;

char ch[maxn][15] ;

int data[maxn] ;



struct mode

{

    int l,r ;

    int value,sum ;

}tree[maxn * 4] ;

//反素数表

int rev[37] = {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 revs[37] = {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 build(int v,int l,int r)

{

    tree[v].l = l ;

    tree[v].r = r ;

    tree[v].value = r-l+1 ;

    if(l == r) return ;

    int mid = (l + r) >> 1 ;

    build(v*2,l,mid) ;

    build(v*2+1,mid+1,r) ;

}



int query(int v,int x)

{

    tree[v].value-- ;

    if(tree[v].l == tree[v].r) return tree[v].l ;

    if(x <= tree[v*2].value)

    return query(v*2,x) ;

    else return query(v*2+1,x-tree[v << 1].value) ;

}

int main()

{

    int n, k;

    while(scanf("%d %d",&n,&k) != EOF)

    {

        int i = 0,maxx = 0,p = 0 ;

        while(rev[i] <= n)

        i++ ;

        p = rev[i-1] ;

        maxx = revs[i-1] ;

        build(1,1,n ) ;

        for(int j = 1 ; j <= n ; j++)

        scanf("%s %d",ch[j],&data[j]) ;

        int yy ;

        for(int i = 0 ; i < p ; i++)

        {

            n -- ;

            yy = query(1,k) ;

            if(n == 0) break ;

            if(data[yy] > 0)

            k = (k-1+data[yy]-1) % n + 1 ;

            else k =( (k-1+data[yy])%n+n) % n+1 ;

        }

        printf("%s %d\n",ch[yy],maxx) ;

    }

    return 0;

}
View Code

 

 

你可能感兴趣的:(get)