P3176 数字串拆分
P3176 停课集训第四题——矩阵维护的神奇DP - 退役倒计时 - 洛谷博客
把不理解的复述了一份。但是还是没理解为啥 \(G\) 可以这么算。。。头一次遇到不用快速幂的矩乘。
你有一个长度为 \(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\)。(以题目为例):
而转移矩阵满足的一个性质是:\(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;
}