601 Divisibility Streaks

Problem 601

对于每个整数 我们可以定义一个函数 ,其中 是最小的 不能被 整除的数。比如

13 可以被 1 整除
14 可以被 2 整除
15 可以被 3 整除
16 可以被 4 整除
17 不可以被 5 整除
所以 。类似的
120 可以被 1 整除
121 不可以被 2 整除
所以 。

是满足下面两个条件的 的和 1. 2.

题目给了两个值 和 帮助验证自想法和实现。

求

首先看下 的数量级,,都接近 long 的数量级了,遍历小于 的每个数显然是不合适的。

整除 ,其实就是被 2,3,4 等整除。手算了 的情况,发现有规律的:

比如 ,那么到了 2 3 4 5 的最小公倍数往后的情况和之前的情况是一样的,循环起来了。

那么如果能计算最小公倍数以内的情况,那么就能很容易得到全部的情况。

不过事与愿违,从 2 到 32 这 31 个数的最小公倍数是一个长达 15 的数字,虽然比 小好几个数量级,但是还是不能遍历。

这条路走不通。

下面是一种直接计算的方式。

什么样的 满足 呢?,否则 。同理, 等等。

类推到 ,最后 。

对于前面 个等式被整除的数都减去 ,,得到一个统一的式子 也就是说, 是 最小公倍数的若干倍。利用这一点,可以容易的找到可能的候选 , 的数量不会很多,对于 等于 31 的情况,粗略估计也只有四位数个吧。

最后利用 得到 ,然后求和。

下面是 函数的代码

static long P(int s, long N)
{
    long lcm = Enumerable.Range(1, s)
        .Select(Convert.ToInt64)
        .Aggregate((cur, next) => Utils.GetLcm(cur, next));

    return Enumerable.Range(1, (int)((N - 2) / lcm))
        .Select(i => i * lcm + 1)
        .Count(c => (c + s) % (s + 1) != 0);
}
Enumerable.Range(1, s),多了个 1,对于最小公倍数没有影响。N-2 的原因是

调用 函数得到和

Enumerable.Range(1, 31).Select(i => P(i, 1L << 2 * i)).Sum();