POJ 2886 - Who Gets the Most Candies?

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开始连续的质数。
  • 反素数p = 2^a + 3^b + 5^c + 7^d中, a < b < c < d。

第一个特性用反证法很好理解,如果一个数的因数不是从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;
}




你可能感兴趣的:(POJ 2886 - Who Gets the Most Candies?)