Processing math: 100%

684 Inverse Digit Sum

Problem 684

s(n) 的定义是数字之和是 n 的最小值,比如 s(10)=19

S(k)=kn=1s(n),题中给出 S(20)=1074,可以用于检测程序是否基本正确。

fi是斐波那契数列。问题是求 90i=2S(fi) 模 1000000007。

斐波那契数列以指数速度增长,所以暴力法肯定是不行的(f90快接近 long 的表示上限了),那么需要在常数或者对数时间复杂度的算出 S(k) 的算法。

s(n) 其实很容易想,数字最小,那么就是高位的值越小越好,那么低位的值都是9即可。

有了 s(n),我们就可以推导一下 S(k) 了。

s(k) 可以写作是 r999999,其中 rk%9,后面 9 的个数是 k/9,不妨记作 L=k/9

那么 S(k) 可以分成两个部分。第一部分(记作 F)是所有长度和 s(k) 一样的数之和,第二部分(记作 R)是数字长度为 1,2,3L 的数之和。 F=10Lri=1i+(10L1)r=10Lr2+3r2r 长度为 L 的数之和和上式类似,其中 r=9L 减一。 R(L)=54×10L19 所以 R=Li=1R(l)=54×10L11019L=6×10L69L 由于 f90 很大,那么 L 也很大,10L 非常大,所以需要利用分治法以对数时间复杂度快速算出其模 1000000007 的值。

private static long GetRemainder(long l)
{
    if (l < 10)
    {
        return (long)Math.Pow(10, l);
    }

    var r = l % 2;
    var half = GetRemainder(l / 2);
    half = (half * half) % Mod;
    if (r == 0)
    {
        return half;
    }
    else
    {
        return (half * 10) % Mod;
    }
}
有了公式,那么就可以快速计算出 S(k) 的值了。
private long S(long f)
{
    long L = f / 9;
    long r = f % 9;

    long remainder = GetRemainder(L);

    long first = (r * r + 3 * r) / 2 * remainder - r;
    long second = 6 * remainder - 6 - 9 * L % Mod;

    return (first + second) % Mod;
}
进而,很容易得出要求的数。
var fibonacci = new List<long>();
long f0 = 0;
long f1 = 1;
for (int i = 2; i <= 90; i++)
{
    long f2 = f0 + f1;
    fibonacci.Add(f2);

    f0 = f1;
    f1 = f2;
}

long result = 0;
foreach (var f in fibonacci)
{
    result += S(f);
}

return result %= Mod;