題意: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;
}