现在的位置: 首页 > 综合 > 正文

POJ 2886 Who Gets the Most Candies?(线段树)

2019年03月02日 ⁄ 综合 ⁄ 共 2356字 ⁄ 字号 评论关闭

题意:N 个小孩围成一圈,他们被顺时针编号为 1 到 N。每个小孩手中有一个卡片,上面有一个非 0 的数字,游戏从第 K 个小孩开始,他告诉其他小孩他卡片上的数字并离开这个圈,他卡片上的数字 A 表明了下一个离开的小孩,如果 A 是大于 0 的,则下个离开的是左手边第
A 个,如果是小于 0 的,则是右手边的第 -A 个小孩。游戏将直到所有小孩都离开,在游戏中,第 p 个离开的小孩将得到 F(p) 个糖果,F(p) 是 p 的约数的个数,问谁将得到最多的糖果。输出最幸运的小孩的名字和他可以得到的糖果。

思路:其实只要知道P=max{F(i)}(1<=i<=N)就可以了,P就是反素数,然后用线段树加速模拟小孩出圈的过程。

对于反素数的求法,先用筛法求出50w以内的素数,并记录下每个数的任意一个因数。现在设f(i)为i的约数个数,如果i是素数,则f(i)=2。如果i是合数,令i=a1^b1*a2^b2*…*an^bn,则i的约数个数为(b1+1)*(b2+1)*...*(bn+1)。因此,可以用递推式f(i)=f(i/ak)*(bk+1)/bk(其中ak是i的任意一个约数)。其实还可以打表记录反素数,这里不作介绍。

线段树的维护,每次用二差查找的方法寻找每次出圈孩子的位置并更新就可以了。具体方法和POJ 2828类似,这里不作详细说明。

注意卡片上的值为正和为负的时候,计算位移时应该分开讨论。

#include<cstdio>
#include<cstring>
#define M ((L+R)>>1)
#define ls (o<<1)
#define rs (o<<1|1)
#define lson ls,L,M
#define rson rs,M+1,R
#define MAXN 500005
char name[MAXN][12];
int n,pos,A[MAXN],sumv[MAXN*3],num;
//sumv[o]储存根o的区间内还剩多少个小孩,初始化时叶子结点为1
//线段树的维护
void pushup(int o) {sumv[o]=sumv[ls]+sumv[rs];}
void build(int o,int L,int R)
{
    if(L==R) {sumv[o]=1;return;}
    build(lson); build(rson);
    pushup(o);
}
void update(int o,int L,int R,int sum)
{
    if(L==R) {sumv[o]=0;num=L;return;}
    sumv[ls]+sum>=pos? update(lson,sum):update(rson,sum+sumv[ls]);
    pushup(o);
}
//==========================================/
//求约数个数部分
int f[MAXN],fmax[MAXN],fmaxnum[MAXN],prime[MAXN],pnum=0,yinshu[MAXN];
bool isprime[MAXN];
void init_fmax()
{
    memset(isprime,0,sizeof(isprime));
    f[1]=fmax[1]=fmaxnum[1]=isprime[0]=isprime[1]=1;
    for(int i=2;i<MAXN;++i)
    {
        if(!isprime[i]) prime[pnum++]=i;
        for(int j=0;j<pnum && prime[j]*i<MAXN;++j)
        {
            isprime[prime[j]*i]=1;
            yinshu[prime[j]*i]=prime[j]; //记录约数
            if(i%prime[j]==0) break;
        }
        if(!isprime[i]) f[i]=2;
        else
        {
            int tn=i,bi=0;
            while(tn>1 && tn%yinshu[i]==0) tn/=yinshu[i],++bi;
            f[i]=f[i/yinshu[i]]*(bi+1)/bi;
        }
        if(f[i]>fmax[i-1]) fmax[i]=f[i],fmaxnum[i]=i;
        else fmax[i]=fmax[i-1],fmaxnum[i]=fmaxnum[i-1];
    }
}
//========================================================/
int main()
{
    init_fmax();
    while(~scanf("%d%d",&n,&pos))
    {
        for(int i=1;i<=n;++i) scanf(" %s %d",name[i],&A[i]);
        build(1,1,n);
        for(int i=1;i<fmaxnum[n];++i)//剩下n-i个人
        {
            update(1,1,n,0);
            if(A[num]<0) pos+=A[num]-1;
            else pos+=A[num]-2;
            while(pos<0) pos+=(n-i);
            pos=pos%(n-i)+1;
        }
        update(1,1,n,0);
        printf("%s %d\n",name[num],fmax[n]);
    }
    return 0;
}

抱歉!评论已关闭.