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\in[1,9]\),可以发现以 \(d\) 为结尾,和为 \(i\) 的数的个数就等于 \(F(i - d)\)。所以我们可以得到:
\[F_i=\sum_{d=1}^{9}F_{i-d}
\]
同样,对于 \(d\in[1,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}
\]
最后直接跑一个矩阵快速幂即可。
#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;
}

浙公网安备 33010602011771号