P17266 [ICPC 2017 Urumqi R] Count Numbers 题解

Solution

传送门

暴力

我们考虑 DP。

设 \(F_i\) 是数位和为 \(i\) 的数字个数,\(G_i\) 是数位和为 \(i\) 的数字的总和。为方便 DP,设 \(F_0=1,G_0=0,F_x=G_x=0(x\in(-\infty, -1])\)。

现在考虑如何转移。我们可以分类讨论最后一位的数字。对于 \(d\in1,9\),可以发现以 \(d\) 为结尾,和为 \(i\) 的数的个数就等于 \(F(i - d)\)。所以我们可以得到:

\F_i=\\sum_{d=1}\^{9}F_{i-d} \\

同样,对于 \(d\in1,9\),可以发现以 \(d\) 为结尾,和为 \(i\) 的数的和就等于没有 \(d\) 的时候的和 \(\times10\) 再加上 \(d\) 的贡献,也就是 \(F_{i-d}\times d\),即 \(G_{i - d}\times10+d\times F_{i-d}\)。所以我们可以得到:

\G_i=\\sum_{d=1}\^9G_{i - d}\\times10+d\\times F_{i-d} \\

但是这样需要 DP \(20^{20}\) 次,会超时。

正解

我们发现这其实是一个线性齐次递推,而且只依赖前 \(9\) 项,所以可以写成矩阵形式。我们注意到 \(G_i\) 依赖 \(F\) 数组,所以要一起写。

状态向量:

\\\begin{bmatrix} F_i \\\\ F_{i-1} \\\\ \\vdots \\\\ F_{i-7} \\\\ F_{i-8} \\\\ G_i \\\\ G_{i-1} \\\\ \\vdots \\\\ G_{i-7} \\\\ G_{i-8} \\\\ \\end{bmatrix} \\

我们现在来研究 DP 矩阵。

左上部分:首先 \(F_{i}=\sum_{d=1}^{9}F_{i-d}\) 所以第一行是 \(9\) 个 \(1\),剩下的 \(8\) 行都是移位矩阵。

右上部分:因为 \(F_{i}\) 和 \(G\) 无关,所以为 \(0\) 矩阵。

右下部分:这是 \(G\) 的转移式里与 \(G\) 有关的部分,即系数都是 \(1\),后面是移位矩阵。

左下部分:系数分别是 \(1\sim9\),后面都是 \(0\)。

总矩阵:

\\\begin{bmatrix} 1 \& 1 \& 1 \& 1 \& 1 \& 1 \& 1 \& 1 \& 1 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \\\\ 1 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \\\\ 0 \& 1 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \\\\ 0 \& 0 \& 1 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \\\\ 0 \& 0 \& 0 \& 1 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \\\\ 0 \& 0 \& 0 \& 0 \& 1 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \\\\ 0 \& 0 \& 0 \& 0 \& 0 \& 1 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \\\\ 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 1 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \\\\ 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 1 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \\\\ 1 \& 2 \& 3 \& 4 \& 5 \& 6 \& 7 \& 8 \& 9 \& 10 \& 10 \& 10 \& 10 \& 10 \& 10 \& 10 \& 10 \& 10 \\\\ 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 1 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \\\\ 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 1 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \\\\ 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 1 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \\\\ 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 1 \& 0 \& 0 \& 0 \& 0 \& 0 \\\\ 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 1 \& 0 \& 0 \& 0 \& 0 \\\\ 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 1 \& 0 \& 0 \& 0 \\\\ 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 1 \& 0 \& 0 \\\\ 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 0 \& 1 \& 0 \\end{bmatrix} \\

最后直接跑一个矩阵快速幂即可。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
using mat = array<array<long long, 18>, 18>;
using vec = array<long long, 18>;
const mat dp = {{
    {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
}};
mat mul(mat A, mat B, long long mod) {
    mat C{};
    for (long long i = 0; i < 18; ++i) {
        for (long long k = 0; k < 18; ++k) {
            if (A[i][k] == 0) continue;
            for (long long j = 0; j < 18; ++j) {
                if (B[k][j] == 0) continue;
                C[i][j] = (C[i][j] + 1ll * A[i][k] * B[k][j] % mod) % mod;
            }
        }
    }
    return C;
}
vec mul(mat A, vec v, long long mod) {
    vec res{};
    for (long long i = 0; i < 18; ++i) {
        for (long long j = 0; j < 18; ++j) {
            res[i] = (res[i] + 1ll * A[i][j] * v[j] % mod) % mod;
        }
    }
    return res;
}
mat qpow(mat bs, __int128_t y, long long mod) {
    mat res{};
    for (long long i = 0; i < 18; ++i) res[i][i] = 1;
    while(y) {
        if(y & 1) res = mul(res, bs, mod);
        y >>= 1;
        bs = mul(bs, bs, mod);
    }
    return res;
}
void solve() {
    long long a, b, p; scanf("%lld%lld%lld", &a, &b, &p);
    __int128_t K = 1;//long long 会爆,所以使用__int128
    for(long long i = 1; i <= b; i++) K *= a;
    printf("%lld\n", mul(qpow(dp, K, p), vec{1}, p)[9] % p);
}
int main() {
    long long t; scanf("%lld", &t);
    while(t--) solve();
    return 0;
}