题目梗概
你有一个长度为 n 的数字串。
定义
你可以将这个数字串分割成若干个数字(允许前导0),将他们的 f() 加起来。
比如 g(123)=f(1+2+3)+f(1+23)+f(12+3)+f(123) 。
已知字符串和m后求答案对 998244353 取模后的值。
解题思路
对于 f() 函数,可以推出
f(n)=∑i=max(0,n−m)n−1f(i)
,转移关系可以构造出矩阵A。
假设我们将S分解为 a1,a2,a3...ak , f(S)=Aa1+a2+a3+...+ak , f(s)=Aa1∗Aa2∗Aa3∗...∗Aak
于是我们就能得到DP, F[i]=F[j]∗now ,在这里 now=Aaj+1...i
但是 now 的次数是高精,所以我预处理 A0...9∗10?
推DP时不断更新 now 就可以了。
#include<cstdio>
#include<cstring>
#define LL long long
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
const int tt=998244353;
int a[505],n,m;
struct jz{
LL x[10][10];
jz(int t=0){cl(x);if (t) for (int i=1;i<=m;i++) x[i][i]=1;}
jz operator*(const jz &b)const{
jz c;
for (int i=1;i<=m;i++)
for (int j=1;j<=m;j++)
for (int k=1;k<=m;k++)
(c.x[i][j]+=x[i][k]*b.x[k][j])%=tt;
return c;
}
jz operator+(const jz &b)const{
jz c;
for (int i=1;i<=m;i++)
for (int j=1;j<=m;j++)
c.x[i][j]=(x[i][j]+b.x[i][j])%tt;
return c;
}
void print(){
for (int i=1;i<=m;i++){
for (int j=1;j<=m;j++) printf("%lld ",x[i][j]);
printf("\n");
}
}
}F[15][505],f[505];
int main(){
freopen("exam.in","r",stdin);
freopen("exam.out","w",stdout);
char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') a[++n]=ch-48,ch=getchar();
scanf("%d",&m);
for (int i=0;i<=n;i++) F[0][i]=jz(1);
for (int i=1;i<=m;i++)
for (int j=1;j<=m;j++)
F[1][0].x[i][j]=i==1||i==j+1;
for (int i=1;i<=n;i++){
F[1][i]=jz(1);
for (int j=1;j<11;j++) F[1][i]=F[1][i]*F[1][i-1];
}
for (int i=2;i<=9;i++)
for (int j=0;j<=n;j++) F[i][j]=F[i-1][j]*F[1][j];
f[0]=jz(1);
for (int i=1;i<=n;i++){
jz now=F[a[i]][0];
for (int j=i-1;j>=0;j--){
f[i]=f[i]+f[j]*now;
if (j) now=now*F[a[j]][i-j];
}
}
printf("%lld\n",f[n].x[1][1]);
return 0;
}