pku2886 Who Gets the Most Candies?(线段树+反素数打表)
pku2886 Who Gets the Most Candies?
题意:
有一排编号为1~N的小孩顺时针围成圈,每人手上有一张编号为a[i]的卡片,游戏从第K个小孩开始,他亮出自己的卡片数字,若a[i]大于0,那么左数第a[i]个小孩出圈,否则右数第a[i]个小孩出圈,游戏一直进行,知道所有孩子都出圈,第p个出圈的将会得到f(p)个糖果,f(p)表示p的因子数,问谁拿到的最多的糖果。
分析:其实我是先知道 了用线段树做,才拼命想该用线段树怎么做的。
思路大致是这样的:每次都默认是从剩下的第一个开始数,顺时针让第count个人出圈。所以必须在每一个人出圈时,算出他左边有多少,再通过他手上的卡片值a[i]算出接下来应该让哪一个人出圈;
至于函数f(p) 就是先打表,算出反素数值,还有算出起因数的个数,之后只需求出最大的f(p),(p<=N),然后算出第p个出圈的人是谁即可。
反素数打表:http://hi.baidu.com/speakless/blog/item/1be8d61b63711cd5ac6e751f.html
用线段树实现过程中有俩个难点:
1):求出每一个刚出圈的人的剩下的左边还有多少人,其实这个可以在算出每一个出圈的人时,顺便计算他左边的人数,看代码就清楚了;
2):让顺时针第count个人出圈之后,应该怎样维护线段树,在线段树中添加一个域num,表示该区间还剩下的人数,所以每出去一个人,对应区间就减一;
#include<iostream> #include<math.h> #define MAXN 500010 using namespace std; const int antiprime[] = {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, 665280}; const int factorNum[] = {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, 224}; struct node { int num; }p[MAXN*4]; struct name { char nm[11]; }na[MAXN]; int a[MAXN]; int n,count,deq; void bulid(int k,int s,int t) { if(s==t) { p[k].num=1; return ; } int kl=k<<1,kr=kl+1,mid=(s+t)>>1; bulid(kl,s,mid); bulid(kr,mid+1,t); p[k].num=p[kr].num+p[kl].num; } void dequeue(int k,int ln,int q,int s,int t) //ln表示左边有多少个数,q表示要删除该区间内的第q个人,[s,t]表示当前区间 { if(s==t) { p[k].num=0; count=ln-1; deq=s; return ; } int kl=k<<1,kr=kl+1,mid=(s+t)>>1; if(p[kl].num>=q)//若左儿子人数不足q个,则要删除的对象在右儿子上,但要注意q的值要相应的减去左儿子的人数 //计算左边人数的时候也类似,若要删除的对象在左儿子上,则在删除对象的左边人数肯定减去右儿子的人数,因为一开始ln表示的是总人数 dequeue(kl,ln-p[kr].num,q,s,mid); else dequeue(kr,ln,q-p[kl].num,mid+1,t); p[k].num=p[kl].num+p[kr].num; } int main() { while(scanf("%d %d",&n,&deq)==2) { for(int i=1;i<=n;i++) scanf("%s %d",na[i].nm,&a[i]); bulid(1,1,n); int tag=0; while(antiprime[tag]<=n)//返回antiprime中小于等于n的最大反素数下标tag tag++; tag--; int cas=antiprime[tag];//第ith个退出的人得到最多的糖; count=deq; while(cas--) { dequeue(1,p[1].num,count,1,n); if(cas==0)//这步不能省的呀,因为后面求余时,p[1].num可能等于0 break; if(a[deq]>0) { count=(count+a[deq]+p[1].num)%p[1].num; if(count==0) count=p[1].num; } else { count=p[1].num-count; count=(count-a[deq]+p[1].num)%p[1].num; if(count==0) count=1; else count=p[1].num-count+1; } } printf("%s %d\n",na[deq].nm,factorNum[tag]); } return 0; }