题目链接:点击打开链接
题意:输入N,K,表示N个小朋友围成一个圈,每个小朋友手拿一张卡片上写着一个数,有正有负,开始时顺时针第K个小朋友出队,然后如果他手里的卡片数m是正数下一个出队的就是他左侧第m个,如果是负的就是右侧第m个,第i个出队的小朋友可以获得 i 的约数个糖果,问所有小朋友都出队后获得最多糖果的是谁,输出名字和糖果数。
本题需要一个知识:反素数,对于一个数n,如果所有小于n的数的约数都没有n多,则n是反素数。具体请百度百科。本题只要提前打表出反素数,然后找出小于N且最接近N的反素数,就可以知道获得最多糖果的是第几个出队的小朋友。
求反素数的代码:(dfs搜索,剪枝需要两条性质,请百度百科反素数)
#include <cstdio> #include <iostream> using namespace std; int prime[20]={1,2,3,5,7,11,13,17,19,23,31,34}; int bestsum,maxnum; int n; void get(int cur,int k,int num,int limit){ int i,temp; if(k>10) return ; if(num>maxnum){ maxnum=num; bestsum=cur; } if(num==maxnum&&cur<bestsum){ bestsum=cur; } temp=cur; for(int i=1;i<=limit;i++){ if(temp*prime[k]>=n) break; temp*=prime[k]; get(temp,k+1,num*(i+1),i); } } int antiprime[50]; int divnum[50]; int main(){ n=500010; for(int i=35;i>=1;i--){ maxnum=0;bestsum=0; get(1,1,1,20); antiprime[i]=bestsum; divnum[i]=maxnum; n=bestsum; } for(int i=1;i<=35;i++){ cout<<antiprime[i]<<" "<<divnum[i]<<endl; } return 0; }
注意设每次要查询的小朋友数为next,当前卡片上的数是正和负时求下一个next方法不一样,如果是正的因为之前位置的人出队了,所以实际查询的人数应该-1,是负的则不受影响。
本题完整代码:
#include <iostream> #include <cstdio> #include <cstring> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define MAX 500010 using namespace std; int antiprime[40]={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}; int divnum[40]={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}; int sum[MAX<<2]; int N,K; char name[MAX][20]; int card[MAX]; void pushup(int rt){ sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void build (int l,int r,int rt){ if(l==r){sum[rt]=1;return ;} int m=(l+r)>>1; build(lson); build(rson); pushup(rt); } int query(int k,int l,int r,int rt){ if(l==r){ sum[rt]--; return l; } int m=(l+r)>>1; int res; if(sum[rt<<1]>=k) res=query(k,lson); else { k-=sum[rt<<1]; res=query(k,rson); } pushup(rt); return res; } void init(){ for(int i=1;i<=N;i++){ scanf("%s%d",name[i],&card[i]); } build(1,N,1); } int main(){ while(~scanf("%d%d",&N,&K)){ init(); int nowP=K; int next=K; int pripos=1; while(antiprime[pripos]<=N) pripos++; pripos--; for(int i=1;i<=antiprime[pripos];i++){ nowP=query(next,1,N,1); if(sum[1]==0)break; if(card[nowP]>0){ next=(card[nowP]%sum[1]+next-1)%sum[1]; } else{ next=(card[nowP]%sum[1]+next)%sum[1]; } if(next<=0)next+=sum[1]; } printf("%s %d\n",name[nowP],divnum[pripos]); } return 0; }