poj 2886 Who Gets the Most Candies?

本文探讨了一种独特的游戏机制,通过数学原理和算法应用,揭示了如何在游戏过程中预测并确定获得最多糖果的孩子。文章深入介绍了游戏规则、反素数的概念以及如何利用线段树等数据结构解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

N 个小孩围成一圈,他们被顺时针编号为 1 到 N。每个小孩手中有一个卡片,上面有一个非 0 的数字,游戏从第 K 个小孩开始,他告诉其他小孩他卡片上的数字并离开这个圈,他卡片上的数字 A 表明了下一个离开的小孩,如果 A 是大于 0 的,则下个离开的是左手边第 A 个,如果是小于 0 的,则是右手边的第 -A 个小孩。游戏将直到所有小孩都离开,在游戏中,第 p 个离开的小孩将得到 F(p) 个糖果,F(p) 是 p 的约数的个数,问谁将得到最多的糖果。输出最幸运的小孩的名字和他可以得到的糖果。

//以上的翻译是从网上找的

首先呢,第P个小孩将得到P的约数个糖果,这就要引入一个数学名词,反素数,反素数的性质就是,比他小的数的约数个数都没他多,那么我们只要在1到n的范围内找到最大的反素数就能知道最多的糖果数,这个其实用反证法就能证明,假设p是1到n内的最大反素数,那么,如果在p到n内还有数的约数比p多,那么显然p就不能算是最大的反素数了,所以我们就先把50万以内的反素数提前打表,他们的约数也提前打好表,这样的话,就不用所有人都出来后才得出最终结果了。

假设p是1到n内最大的反素数,我们只需模拟p次这个出队过程了,然后有一个需要注意的地方是,下一个人的位置怎么求,这里面有一种是相对位置,还有一种是最初位置,很显然,由于数据量太大,不能通过简单的模拟来实现最初位置和相对位置的转化,这里面,能直接求出来的是相对位置,假设一个人的位置是k,手上卡片的数字是+m,那么下一个人的位置应该是p+m,但是由于他的出队,这个相对位置就要减1了,是p+m-1,但是如果一个人的卡片是-m,此时他的退出就对下一个人的相对位置没有影响了,因为下一个人的位置应该是p-m,在这个人之前,就没有影响了。然后需要注意的是可能会出现负数和0,这就要适当的处理一下了。

然后就是相对位置与最初位置的转换,这里用到了线段树,线段树所附加的信息是该区间还有多少人,每踢掉一个人,区间值变化一次,然后就相当于一个动态的队列了,中间踢掉某个人,整个序列的长度减1,每个人的位置发生了变化,然后再踢相对位置为p的人,只要从头数到第p个人,踢掉就行了。线段树的话,我们先从叶子节点看,踢掉一个人,就相当于这个叶子节点的值就变成0了,然后从1到n,数叶子节点为1的个数,到p的时候把踢掉该位置的人就行了,这样就好理解点了,然后根据点更新到段就能实现快速的插入了。


/* ID: sdj22251 PROG: subset LANG: C++ */ #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> #define LOCA #define MAXN 500005 #define INF 100000000 #define eps 1e-7 #define L(x) x<<1 #define R(x) x<<1|1 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}; const int factor[] = {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}; struct node { int left, right, mid; int cnt; }tree[4 * MAXN]; struct child { char name[25]; int v; }p[MAXN]; void make_tree(int s, int e, int C) { tree[C].left = s; tree[C].right = e; tree[C].mid = (s + e) / 2; tree[C].cnt = tree[C].right - tree[C].left + 1; if(s == e) return; make_tree(s, tree[C].mid, L(C)); make_tree(tree[C].mid + 1, e, R(C)); } int update(int C, int k) { tree[C].cnt--; if(tree[C].left == tree[C].right) { return tree[C].left; } if(tree[L(C)].cnt >= k) return update(L(C), k); else return update(R(C), k - tree[L(C)].cnt); } int main() { #ifdef LOCAL freopen("d:/data.in","r",stdin); freopen("d:/data.out","w",stdout); #endif int n, k, i; while(scanf("%d%d", &n, &k) != EOF) { for(i = 1; i <= n; i++) { scanf("%s%d", p[i].name, &p[i].v); } make_tree(1, n, 1); int cnt = 0; while(antiprime[cnt] <= n) cnt++; cnt--; int pos = 0; p[pos].v = 0; for(i = 0; i < antiprime[cnt]; i++) { if(p[pos].v > 0) { k = ((k + p[pos].v - 2) % tree[1].cnt + tree[1].cnt) % tree[1].cnt + 1; } else { k = ((k + p[pos].v - 1) % tree[1].cnt + tree[1].cnt) % tree[1].cnt + 1; } pos = update(1, k); } printf("%s %d\n", p[pos].name, factor[cnt]); } return 0; }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值