Advanced Data Structures :: Segment Tree
Description
有N个孩子围成一圈,每个人手里拿着一张小纸张,里面有一个整数A(可正可负)。
指定一个K,从第K个孩子开始,一个个出圈。
第K个孩子出圈之后,则往右边数第A个孩子则为下一个出圈的娃(A为负数就是向左数第-A个孩子)。
孩子出圈后,他是第p个出来的,就可以拿到p这数字,然后得到F(p)分。
F(p)是什么呢,是p的因数的个数。
输入N、K和每个孩子的名字,手里拿的整数大小。
输出得分最多的孩子,如果有多个得分一样多的,输出第一个拿那么多分的孩子。
Type
Advanced Data Structures :: Segment Tree
Analysis
一看这道题,靠,约瑟夫啊。
再一看这数据规模……
裸模拟约瑟夫是过不了的。
这时候线段树模拟约瑟夫便站了出来。
线段树如何模拟约瑟夫呢,是这样的。
线段树的所有叶子结点,初始化为1,表示对应位置的孩子都还在。
然后一个个出圈,对应叶子赋值为0。
这时候,我们有了孩子手中的数字A,便可以利用线段树快速找下一个孩子。
A对剩下的孩子的个数取模,便可以找到下一个出圈的应该是第几个孩子。
然后我们利用线段树区间求和的能力,找到这个第几个孩子,让他出来即可。
完了吗?没有。
对于孩子的得分,其实是一个反素数的问题。
反素数,就是从小到大数,因数相同个数中,第一个出现的数字。
他有两个特性:
第一个特性用反证法很好理解,如果一个数的因数不是从2开始连续的质数,那么我们就可以找到其中一个因数,用更小的质数代替他,必然可以得到比他更小的数,并且因数个数相等。
对于第二个特性也一样,一定可以找到更小的因数来代替,使得这个数更小,且因数个数相同。
由此一来,我们只要找到小于等于孩子个数的最大反素数即可。
不过,因为第37个反素数就已经大到超过数据规模,我们只要写个小程序暴力枚举每个数,
找到反素数,然后打一张表,搞定。
所以上面的都是废话。
Solution
// POJ 2886 // Who Gets the Most Candies // by A Code Rabbit #include <cstdio> #include <cstring> #define LSon(x) ((x) << 1) #define RSon(x) ((x) << 1 | 1) const int MAXN = 500002; const int ROOT = 1; const int LEN_NAME = 12; struct Seg{ int w; }; struct Child { char name[LEN_NAME]; int number; }; struct SegTree { Seg node[MAXN << 2]; void Update(int pos) { node[pos].w = node[LSon(pos)].w + node[RSon(pos)].w; } void Build(int l, int r, int pos) { if (l == r) { node[pos].w = 1; return; } int m = l + r >> 1; Build(l, m, LSon(pos)); Build(m + 1, r, RSon(pos)); Update(pos); } Child Remove(int l, int r, int pos, int x, Child* child) { if (l == r) { node[pos].w = 0; return child[l]; } int m = l + r >> 1; Child res; if (x < node[LSon(pos)].w) res = Remove(l, m, LSon(pos), x, child); else res = Remove(m + 1, r, RSon(pos), x - node[LSon(pos)].w, child); Update(pos); return res; } }; const int anti_prime[] = { 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 n, k; Child child[MAXN]; SegTree tree; char max_name[LEN_NAME]; int Compute(int x); int main() { while (scanf("%d%d", &n, &k) != EOF) { // Intput. for (int i = 0; i < n; ++i) { getchar(); scanf("%s%d", child[i].name, &child[i].number); } // Solve. tree.Build(0, n - 1, ROOT); int pos_insert = k; int num_remain = n; int top_anti_prime = 0; while (num_remain) { Child child_jump_out = tree.Remove(0, n - 1, ROOT, pos_insert - 1, child); num_remain--; if (anti_prime[top_anti_prime] == n - num_remain) { strcpy(max_name, child_jump_out.name); top_anti_prime++; } if (num_remain) { int number = child_jump_out.number; if (number > 0) { pos_insert = (pos_insert - 1 + number - 1) % num_remain + 1; } else { number = (-number) % num_remain; pos_insert = (pos_insert + num_remain - number - 1) % num_remain + 1; } } } // Output. printf("%s %d\n", max_name, Compute(anti_prime[top_anti_prime - 1])); } return 0; } int Compute(int x) { int result = 0; for (int i = 1; i * i <= x; ++i) { if (x % i == 0) { result += i * i == x ? 1 : 2; } } return result; }