684 Inverse Digit Sum

Problem 684

的定义是数字之和是 的最小值,比如

,题中给出 ,可以用于检测程序是否基本正确。

是斐波那契数列。问题是求 模 1000000007。

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

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

有了 ,我们就可以推导一下 了。

可以写作是 ,其中 ,后面 9 的个数是 ,不妨记作

那么 可以分成两个部分。第一部分(记作 )是所有长度和 一样的数之和,第二部分(记作 )是数字长度为 的数之和。 长度为 的数之和和上式类似,其中 减一。 所以 由于 很大,那么 也很大, 非常大,所以需要利用分治法以对数时间复杂度快速算出其模 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;
    }
}
有了公式,那么就可以快速计算出 的值了。
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;