先处理函数 $f_i$,有 $f_i = \sum \limits _{j = i - m}^{i - 1} f_j$,这个递推式显然可以通过矩阵乘法进行优化。设 $F_i$ 表示通过递推函数 $f_i$ 得到的矩阵,则有以下矩阵的递推(以 $m = 5$ 为例):
$g_i$ 的计算不太好处理,等价转换以下令 $G_i$ 表示处理前 $i$ 位得到的所有情况的矩阵之和,因此最后的答案就会在 $G_n$ 中。由矩阵的乘法分配律可知 $A^{x + y} = A^x \times A^y$,因此可以将 $G_i$ 进行转移。设 $A_{i,j}$ 表示数字串中 $[i,j]$ 的转移矩阵之积,则 $G_i = \sum \limits _{j = 0}^{i - 1} G_j \times A_{j + 1,i}$。
现在的复杂度为计算矩阵 $A_{i,j}$,不难发现 $A_{i,j} = F_{s_i^{10^{n - i}}} \times A_{i + 1,j}$,稍作变换得 $A_{i,j} = F_{({10^{n - i}})^{s_i}} \times A_{i + 1,j}$。这样只需要预处理出 $F_{10^i}$ 的矩阵即可。
两个小细节:
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #define ll long long #define ull unsigned long long #define INF 0x3f3f3f3f using namespace std; const int MAX = 505; const int MOD = 998244353; int m,n;ll ans; char s[MAX]; struct Mat { ll a[6][6]; Mat() {memset(a,0,sizeof(a));} Mat operator * (const Mat &y) { Mat z; for (int k = 1;k <= m;++k) for (int i = 1;i <= m;++i) for (int j = 1;j <= m;++j) z.a[i][j] += a[i][k] * y.a[k][j] % MOD,z.a[i][j] %= MOD; return z; } Mat operator + (const Mat &y) { Mat z; for (int i = 1;i <= m;++i) for (int j = 1;j <= m;++j) z.a[i][j] = a[i][j] + y.a[i][j],z.a[i][j] %= MOD; return z; } Mat qpow (Mat x,ll y) { Mat res; for (int i = 1;i <= m;++i) res.a[i][i] = 1; while (y) { if (y & 1) res = res * x; x = x * x; y >>= 1; } return res; } } p[MAX],f[MAX][MAX],g[MAX]; int main () { scanf ("%s%d",s + 1,&m); n = strlen (s + 1); for (int i = 1;i <= m;++i) p[0].a[1][i] = 1; for (int i = 2;i <= m;++i) p[0].a[i][i - 1] = 1; for (int i = 1;i <= n;++i) p[i] = p[i].qpow (p[i - 1],10); for (int j = 1;j <= n;++j) for (int i = j;i;--i) f[i][j] = (i == j) ? f[i][j].qpow (p[0],s[i] - '0') : f[i + 1][j] * f[i][j].qpow (p[j - i],s[i] - '0'); for (int i = 1;i <= m;++i) g[0].a[i][i] = 1; for (int i = 1;i <= n;++i) for (int j = 0;j < i;++j) g[i] = g[i] + (g[j] * f[j + 1][i]); printf ("%lld\n",g[n].a[1][1]); return 0; }
|