题意:模拟约瑟夫环。有N(1<=N<=500000)个孩子围成一圈,他们被顺时针编号为 1 到 N。每个小孩手中有一个卡片,上面有一个非 0 的数字,游戏从第 K 个小孩开始,他告诉其他小孩他卡片上的数字并离开这个圈,他卡片上的数字 A 表明了下一个离开的小孩,如果 A 是大于 0 的,则下个离开的是左手边第 A 个,如果是小于 0 的,则是右手边的第 A 个小孩。游戏将直到所有小孩都离开,在游戏中,第 p 个离开的小孩将得到 F(p) 个糖果,F(p) 是 p 的约数的个数,问谁将得到最多的糖果。输出最幸运的小孩的名字和他可以得到的糖果。
F(p)的值直接暴力跑一下就可以了。
因为A的值可能很大,所以会用到模运算,这就要求下标是从0开始,但是小孩的编号从1开始,所以在更新的时候,要考虑两者的转换,即从编号下标从1开始的,转换成编号下标从0开始的,再转换回去。
// Time 1563ms; Memory 24196K
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #define maxn 1<<20 #define mm 500010 using namespace std; int tmp[mm],d[mm],g[mm]; char s[mm][12]; int size,n,cnt,dx; struct line { int l,r; int m; }a[maxn]; void init() { int i; for(n=1;n<size;n<<=1); for(i=n;i<2*n;i++) { a[i].l=a[i].r=i-n+1; if(i-n<size) a[i].m=1; else a[i].m=0; } for(i=n-1;i>0;i--) { a[i].l=a[2*i].l; a[i].r=a[2*i+1].r; a[i].m=a[2*i].m+a[2*i+1].m; } } void insert(int i,int x) { if(a[i].l==a[i].r) { tmp[a[i].l-1]=++cnt; a[i].m=0; dx=a[i].l; return; } if(a[2*i].m>=x) insert(2*i,x); else insert(2*i+1,x-a[2*i].m); a[i].m--; } void calu() //建表,得到约数个数 { int i,j,limit; limit=(int)sqrt(mm*1.0); for(i=1;i<=limit;i++) { for(j=i+1;j*i<=mm;j++) g[i*j]+=2; g[i*i]++; } } int main() { int i,k,y,p,mx; calu(); while(scanf("%d%d",&size,&k)!=EOF) { init(); cnt=0; memset(tmp,0,sizeof(tmp)); for(i=0;i<size;i++) scanf("%s%d",s[i],&d[i]); p=size; for(;p;) { insert(1,k); p--; if(p==0) break; if(d[dx-1]>0) k=((k-2+d[dx-1]%p)%p+p)%p+1; //得到要插入(删除)的是第几个 else k=((k-1+d[dx-1]%p)%p+p)%p+1; } mx=0; for(i=0;i<size;i++) { if(g[tmp[mx]]<g[tmp[i]]) mx=i; else if(g[tmp[mx]]==g[tmp[i]] && tmp[mx]>tmp[i]) mx=i; } printf("%s %d\n",s[mx],g[tmp[mx]]); } return 0; }