P3176 数字串拆分

P3176 停课集训第四题——矩阵维护的神奇DP - 退役倒计时 - 洛谷博客

把不理解的复述了一份。但是还是没理解为啥 \(G\) 可以这么算。。。头一次遇到不用快速幂的矩乘。

P3176 [HAOI2015] 数字串拆分

你有一个长度为 \(n\) 的数字串 \(s_0\)

定义 \(f(s)\) 为将 \(s\) 拆分成若干个 \(1 \sim m\) 的数的和的方案数,比如 \(m=2\) 时,\(f(4)=5\),分别为 \(4=1+1+1+1=1+1+2=1+2+1=2+1+1=2+2\)

定义 \(g(s)\) 为将 \(s\) 这个数字串分割成若干个数字(允许前导 \(0\)),设他们的和为 \(x\),则 \(g(s)\) 为所有情况下 \(f(x)\) 之和。比如 \(g(123)=f(1+2+3)+f(1+23)+f(12+3)+f(123)\)

给定 \(s_0\)\(m\),求 \(g(s)\)

这道题,先考虑 \(f(n)\) 的求法,显然\(f(n) = f(n-1) + f(n-2) + ... + f(n-m)\),也就是一个 \(m\) 阶递推数列,\(f[0\cdots m] = 1\)。(以题目为例):

\[ F_{i}= \begin{bmatrix} f_{i - 3} & f_{i - 2} & f_{i - 1} & f_i \end{bmatrix} \]
\[ F_{i+1} = \begin{bmatrix} f_{i - 2} & f_{i - 1} & f_i & f_{i + 1} \end {bmatrix} = \begin{bmatrix} f_{i - 3} & f_{i - 2} & f_{i - 1} & f_i \end{bmatrix} \cdot \begin{bmatrix} 0 & 0 & 0 & 1\\ 1 & 0 & 0 & 1\\ 0 & 1 & 0 & 1\\ 0 & 0 & 1 & 1 \end {bmatrix} \]

而转移矩阵满足的一个性质是:\(A^{n+m}=A^m\times A^n\),那么 \(f(a + b + c + ...)\) 使用的转移矩阵就能写成 \(A^{a}\times A^{b}\times A^{c}\times ...\)

定义 \(G_n\) 表示 \(s\) 前缀 \(n\) 个数构成的数字串的所有种类的 \(F\) 的转移矩阵的和;

定义 \(D_{i,j}=A^k\)\(k\)\(s\) 中第 \(i\)\(j\) 位拼成的数字,即 \(F_k\) 的转移矩阵。以题面为例,\(s=123\) 则有:

\(G_1=A^1=D_{1,1}\) 。即原式 \(g_1=f_1\)

\(G_2=(A^1\times A^2)+(A^{12})=G_1\times D_{2,2}+D_{1,2}\)。即原式 \(g_{12}=f_{1+2}+f_{12}\)

\(G_3=(A^1\times A^2\times A^3)+(A^1\times A^{23})+(A^{12}\times A^3)+A^{123}=G_1\times D_{2,3}+G_2\times D_{3,3}+D_{1,3}\)。即原式\(g_{123}=f_{1+2+3}+f_{1+23}+f_{12+3}+f_{123}\)

仔细观察有:\(G_i = \sum\limits G_j * D_{j + 1,i}\)

我们发现预处理出 \(D_{i,j}\) 即可,就可以递推,结果矩阵 \(G_n\) 的第一行之和即为答案(???)。

#include <bits/stdc++.h>
using std::cin;
using std::cout;
using LL = long long;
using ull = unsigned long long;
using ld = long double;

const int maxn = 505,mod = 998244353;

int n,m;
struct mat {
    LL a[5][5];
    LL* operator[](int b) {
        return a[b];
    }
    mat operator*(mat b) {
        mat c;
        memset(c.a,0,sizeof(c.a));
        for(int k=0; k<m; k++)
            for(int i=0; i<m; i++)
                for(int j=0; j<m; j++)
                    c[i][j]=(a[i][k]*b[k][j]%mod+c[i][j])%mod;
        return c;
    }
    mat operator+(mat b) {
        mat c;
        memset(c.a,0,sizeof(c.a));
        for(int i=0; i<m; i++)
            for(int j=0; j<m; j++)
                c[i][j]=(a[i][j]+b[i][j])%mod;
        return c;
    }
}mi[15],D[510][510],g[510];

mat pow10(mat x){
    x=x*x; // x^2
    mat s=x; // s=x^2
    x=x*x;x=x*x; // x=x^8
    x=x*s; // x=x^10
    return x;
} // x^10 指数进一位

char s[maxn];
int main() {
    cin >> s+1 >> m;
    n=strlen(s+1);
    for(int i=1;i<m;i++) mi[1].a[i][i-1]=1;
    for(int i=0;i<m;i++) mi[1].a[i][m-1]=1;
    for(int i=0;i<m;i++) g[0].a[i][i]=mi[0].a[i][i]=1; // A^0 单位矩阵
    for(int i=2;i<=10;i++) mi[i]=mi[i-1]*mi[1]; // 处理矩阵 A^1 ~~ A^10

    // 预处理 D
    for(int i=1;i<=n;i++){
        D[i][i] = mi[s[i]-'0'];
        for(int j=i+1;j<=n;j++){
            D[i][j] = pow10(D[i][j-1])*mi[s[j]-'0'];
        }
    }

    for(int i=1;i<=n;i++){
        for(int j=i-1;j>=0;j--){
            g[i]=g[i]+g[j]*D[j+1][i];
        }
    }
    LL ans=0;
    for(int i=0;i<m;i++) ans=(ans+g[n].a[0][i])%mod;
    cout << ans;
    return 0;
}