n个人围成圈,每个人手上有一个数字,最开始k跳出圈,如果k手上的数num是正数,则向左num个人出圈,负数就向右,直到所有人都出圈,得分最高的是第(1-n中因子数最多的数)次跳出的人
首先处理1-n中因子最多的数,有个名词叫反素数,不管他这里直接打表
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int mx = 6e5 + 5;
int cnt[mx], anti_prim[mx], num[mx], tot = 0;
int main() {
freopen("out.txt", "w", stdout);
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i <= 600000; i++) {
for (int j = 1; j*j <= i; j++) {
if (i % j == 0) {
if (j*j == i) cnt[i] += 1;
else cnt[i] += 2;
}
}
}
int max_num = 0;
for (int i = 1; i <= 600000; i++) {
if (cnt[i] > max_num) {
anti_prim[++tot] = i;
num[tot] = cnt[i];
max_num = cnt[i];
}
}
for (int i = 1; i <= tot; i++) {
printf("%d,",anti_prim[i]);
}
for (int i = 1; i <= tot; i++) {
printf("%d,",num[i]);
}
return 0;
}
接着是处理跳出次序问题,用树状数组记录每个位置的人是否已经跳出,刚开始看到题解说二分我以为是把人按1-n排列,第k个人跳出就从k开始向左或向右进行二分出下一个跳出的人的位置,但是这样会有问题,比如第k个人手上是5,那么应该向右二分出5个人,但是有可能右边不足五个人需要绕回到左边,所以这样是行不通的
正确的做法是把人按1-n排列,然后每次有人跳出就通过公式计算出下一个人是从1-n中还在圈内的第几个,这样就可以二分1-n了
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int mx = 5e5 + 5;
int anti_prim[] = {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 num[] = {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 n, k, c[mx];
struct node {
char name[20];
int value;
}stu[mx];
int lowbit(int x) {
return x & -x;
}
void add(int x, int val) {
for (int i = x; i <= n; i+=lowbit(i)) {
c[i] += val;
}
}
int sum(int x) {
int ans = 0;
for (int i = x; i > 0; i-=lowbit(i)) {
ans += c[i];
}
return ans;
}
int bit_search(int x) {
int left = 1, right = n, mid;
while (left < right) {
mid = (left + right) / 2;
if (sum(mid) >= x) right = mid;
else left = mid + 1;
}
return left;
}
int main() {
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
while(scanf("%d%d", &n, &k) != EOF) {
memset(c, 0, sizeof(c));
for (int i = 1; i <= n; i++) {
scanf("%s %d", stu[i].name, &stu[i].value);
add(i, 1);
}
int pos = lower_bound(anti_prim, anti_prim + 36, n) - anti_prim;
if (anti_prim[pos] > n) pos--;
int now = k, next = k, remaining = n - 1, cnt = 1, ans;
add(k, -1);
while (true) {
if (cnt == anti_prim[pos]) {
ans = now;
break;
}
int m = stu[now].value;
if (m > 0) next = (next - 1 + m) % remaining;
else next = ((next + m) % remaining + remaining) % remaining;
if (next == 0) next = remaining;
remaining--; cnt++;
now = bit_search(next);
add(now, -1);
}
printf("%s %d\n", stu[ans].name, num[pos]);
}
return 0;
}