思路:
先打反素数表,即可确定因子最多的那个数。然后模拟踢人的过程确定对应的人名。模拟的过程使用线段树优化加速。
实现:
1 #include <cstdio> 2 #include <cstring> 3 #include <string> 4 #include <algorithm> 5 using namespace std; 6 7 const int MAXN = 500005; 8 9 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}; 10 int fac[] = {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}; 11 12 int tree[MAXN << 2], num[MAXN], n, k; 13 char name[MAXN][11]; 14 15 void build(int num, int l, int r) 16 { 17 if (l == r) { tree[num] = 1; return; } 18 int m = l + r >> 1; 19 build(num << 1, l, m); 20 build(num << 1 | 1, m + 1, r); 21 tree[num] = tree[num << 1] + tree[num << 1 | 1]; 22 } 23 24 int kick(int num, int l, int r, int x) // 在[l, r]范围内踢掉第x个人 25 { 26 if (l == r) { tree[num] = 0; return l; } 27 int m = l + r >> 1, tmp = tree[num << 1], ans = -1; 28 if (tmp >= x) ans = kick(num << 1, l, m, x); 29 else ans = kick(num << 1 | 1, m + 1, r, x - tmp); 30 tree[num] = tree[num << 1] + tree[num << 1 | 1]; 31 return ans; 32 } 33 34 int main() 35 { 36 while (scanf("%d %d", &n, &k) != EOF) 37 { 38 for (int i = 1; i <= n; i++) scanf("%s %d", name + i, num + i); 39 build(1, 1, n); 40 int now = k, out = -1, cnt = n; 41 int tmp = lower_bound(antiprime, antiprime + 36, n) - antiprime; 42 int w = antiprime[tmp] > n ? tmp - 1 : tmp; 43 for (int i = 0; i < antiprime[w]; i++) 44 { 45 out = kick(1, 1, n, now); 46 cnt--; 47 if (!cnt) break; 48 if (num[out] > 0) now = ((now + num[out] - 1) % cnt + cnt) % cnt; 49 else now = ((now + num[out]) % cnt + cnt) % cnt; 50 now = !now ? cnt : now; 51 } 52 printf("%s %d\n", name[out], fac[w]); 53 } 54 return 0; 55 }