http://poj.org/problem?id=2886
一群孩子从编号1到n按顺时针的方向围成一个圆,每个孩子手中卡片上有一个数字,首先是编号为k的孩子出去,如果他手上的数字m是正数,那么从他左边(顺时针)开始第m个孩子出去,如果是负的
那么从他的右边(也就是逆时针)开始第m个孩子出去~~~一直到所有的孩子出去,另外,第p个出去的孩子可以得到的糖果数量是p的约数个数,问能得到最多糖果的孩子的名字和得到的糖果数目
关于公约数最多的问题,可以利用到反素数,可以首先先打表反素数和对应的约数个数,找出约数最多的次数p,p肯定是不大于n的,然后就是模拟孩子出去的情况,只要模拟p次就行
然后用线段树模拟,与上一题插队差不多,记录下每个区间的人数,每次的顺序k表示第k个有人的区间更新为空
code
1 #include<cstdio> 2 using namespace std; 3 struct point { 4 int l,r; 5 int mark;//记录每个区间人数 6 }; 7 point tree[500001*4]; 8 char jjc[500001][10]; 9 int a[500001],pos; 10 int prime[]={ //反素数 11 1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,1260,1680,2520,5040,7560,10080,15120, 12 20160,25200,27720,45360,50400,55440,83160,110880,166320,221760,277200,332640,498960, 13 554400 14 }; 15 int numb[]={ //对应的约数个数 16 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, 17 144,160,168,180,192,200,216 18 }; 19 void build (int i,int left,int right) 20 { 21 tree[i].l=left,tree[i].r=right; 22 tree[i].mark=tree[i].r-tree[i].l+1; 23 if (left==right) { return ;} 24 int mid=(left+right)/2; 25 build(i*2,left,mid); 26 build(i*2+1,mid+1,right); 27 } 28 void update(int i,int ans) 29 { 30 if (tree[i].l==tree[i].r) 31 { 32 pos=tree[i].l; 33 tree[i].mark--; 34 return ; 35 } 36 if (ans<=tree[i*2].mark) 37 update(i*2,ans); 38 else 39 update(i*2+1,ans-tree[i*2].mark); 40 tree[i].mark=tree[i*2].mark+tree[i*2+1].mark; 41 } 42 int main() 43 { 44 int n,k,i,w,m; 45 while (scanf("%d %d",&n,&k)==2) 46 { 47 for (i=1;i<=n;i++) 48 scanf("%s %d",&jjc[i],&a[i]); 49 build(1,1,n); 50 w=0; 51 for(i=0;prime[i]<=n;i++)w=i; 52 pos=0;m=prime[w]; 53 a[0]=0; 54 while (m--) 55 { 56 int num=tree[1].mark; 57 if (a[pos]>0) 58 k=((k+a[pos]-2)%num+num)%num+1; 59 else 60 k=((k+a[pos]-1)%num+num)%num+1; 61 update(1,k); 62 } 63 printf("%s %d\n",jjc[pos],numb[w]); 64 } 65 return 0; 66 }