您的位置:首页 > 其它

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

2013-08-03 17:44 483 查看
题意: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
;++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
);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: