先打表求反素数,这些反素数表示能够得到最多的糖果的回合数。
因此只要找出n以内最大的反素数,就表示在那个回合出局的人拿到最多的糖果,糖果数为该反素数的因子个数。
用线段树维护圈内的剩余人数和原来的位置,用类似约瑟夫环的方式求出每个人的相对位置,更新线段树,找出在那一个回合出局的人。
tree[1]表示剩下的人数。
当某个人手中的数字为负时,有可能会极小,因此要注意使余数为正。
#include <cstdio>
#define lchild rt << 1, l, m
#define rchild rt << 1 | 1, m + 1, r
const int maxn = 500000 + 10;
const int sz = 4 * maxn;
int anti_prime[35] = {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};
int fact[35] = {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};
struct Child {
char name[20];
int val;
};
Child child[maxn];
int tree[sz];
int n, k;
void build(int rt = 1, int l = 1, int r = n) {
tree[rt] = r - l + 1;
if (l == r) {
return;
}
int m = (l + r) >> 1;
build(lchild);
build(rchild);
}
//读入相对位置,返回原位置
int update(int pos, int rt = 1, int l = 1, int r = n) {
tree[rt]--;
if (l == r) {
return l;
}
int m = (l + r) >> 1;
if (pos <= tree[rt<<1]) {
return update(pos, lchild);
} else {
return update(pos - tree[rt<<1], rchild);
}
}
int main(int argc, char const *argv[]) {
scanf("%d%d", &n, &k);
build();
for (int i = 1; i <= n; i++) {
scanf("%s%d", child[i].name, &child[i].val);
}
int num, cnt;
for (int i = 0; i < 35; i++) {
if (anti_prime[i] > n) {
break;
}
num = anti_prime[i];
cnt = fact[i];
}
int pos = 0;
//进行num个回合
for (int i = 0; i < num; i++) {
if (child[pos].val > 0) {
k = (k - 1 + child[pos].val - 1) % tree[1] + 1;
} else {
k = ((k - 1 + child[pos].val) % tree[1] + tree[1]) % tree[1] + 1;
}
pos = update(k);
}
printf("%s %d\n",child[pos].name, cnt);
return 0;
}