题意:N 个小孩围成一圈,他们被顺时针编号为 1 到 N。每个小孩手中有一个卡片,上面有一个非 0 的数字,游戏从第 K 个小孩开始,他告诉其他小孩他卡片上的数字并离开这个圈,他卡片上的数字 A 表明了下一个离开的小孩,如果 A 是大于 0 的,则下个离开的是左手边第 A 个,如果是小于 0 的,则是右手边的第 -A 个小孩。游戏将直到所有小孩都离开,在游戏中,第 p 个离开的小孩将得到 F(p) 个糖果,F(p) 是 p 的约数的个数,问谁将得到最多的糖果。输出最幸运的小孩的名字和他可以得到的糖果。
思路:看到这题感觉完全摸不清头绪,只好去参考大神的代码,看完才知道还有反素数这玩意,对应一个y,任意x<y都满足F(x)<F(y),这样的y就是反素数,明显的DP问题,可以先DP一下,然后直接打表。现在只要找到最大的小于等于n的反素数,这个数就是第几个出去的小孩是幸运小孩。那个卡片的问题,对于 k 位置的 孩子,他的 数字是正数 那么因为他自己本身是要被踢走的,所以相对位置 为k= k+num-1如果数字是负数,那么按正着数就没影响,k=k-num。线段树存储当前区间共有多少个人,每一次找到第k (前面有k-1个)个孩子,经过的区间都要 -1,然后记录被踢走的孩子编号。
线段树的值存储这个区间还剩下的小孩数目,其余的全部和2828是一模一样了。
#include<cstdio>
#include<cstring>
#include<iostream>
#define L(u) (u<<1)
#define R(u) (u<<1|1)
using namespace std;
const int N = 500001;
char name[N][11];
int card[N];
int rfact[37]={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,500001};
int candy[37]={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,1314521};
struct Node{
int r,l,sum;
};
Node node[N<<2];
void build(int u,int left,int right)
{
node[u].l=left; node[u].r=right;
if(left==right)
{
node[u].sum = 1;
return;
}
int mid = (left+right)>>1;
build(L(u),left,mid);
build(R(u),mid+1,right);
node[u].sum = right-left+1;
}
int query(int u,int num)
{
--node[u].sum;
if(node[u].l==node[u].r)
return node[u].l;
if(num<=node[L(u)].sum)
return query(L(u),num);
return query(R(u),num-node[L(u)].sum);
}
int main(void)
{
int n,k,p,sum;
while(~scanf("%d %d",&n,&k))
{
int i = 0;
while(rfact[i]<=n)//求反素数的最大的
i++;
i -= 1;
p = rfact[i];
sum = candy[i];
build(1,1,n);
for(i=1;i<=n;++i)
scanf("%s %d",name+i,card+i);
k--;
int num;
for(i=1;i<=p;++i)
{
n--;//人数每次减一
num = query(1,k+1);
if(i==p)
break;
if(card[num]>0)
k = (k+card[num]-1)%n;
else
k = ((k+card[num])%n+n)%n;
}
printf("%s %d\n",name[num],sum);
}
return 0;
}
本文探讨了一种独特的小孩糖果分配游戏规则,通过利用反素数的概念和线段树的数据结构,解决如何确定谁将是得到最多糖果的幸运小孩。详细介绍了游戏规则、关键数学概念、算法实现和优化策略。
868

被折叠的 条评论
为什么被折叠?



