题解 P3176 [HAOI2015]数字串拆分
P3176 [HAOI2015]数字串拆分
题意简述:定义
f(s) 为将s 拆分成若干个不大于m 个数的方案数。给出数字字符串t ,求g(t)=\sum f(x) ,其中x 为将t 分割成若干个数后它们的和。例如t=\texttt{12} 时,答案为f(1+2)+f(12) 。本文节选自 DP 大锅乱炖 IV.
注意到
不难求出转移方程
注意到
/*
Powered by C++11.
Author : Alex_Wei.
*/
#include <bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(3)
//#define int long long
using ll = long long;
#define mem(x,v) memset(x,v,sizeof(x))
const int N=500+5;
const int M=5;
const int mod=998244353;
int n,m;
char s[N];
void add(int &x,ll y){
x+=y%mod;
if(x>mod)x-=mod;
}
struct mat{
int a[M][M];
friend mat operator * (mat x,mat y){
mat z; mem(z.a,0);
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
for(int k=0;k<m;k++)
add(z.a[i][j],1ll*x.a[i][k]*y.a[k][j]);
return z;
}
friend mat operator + (mat x,mat y){
mat z; mem(z.a,0);
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
add(z.a[i][j],x.a[i][j]+y.a[i][j]);
return z;
}
}base,f[N],c[N][N],d[N][10];
mat ksm(mat a,int b){
mat x; mem(x.a,0);
for(int i=0;i<m;i++)x.a[i][i]=1;
while(b){
if(b&1)x=x*a;
a=a*a,b>>=1;
} return x;
}
int main(){
scanf("%s%d",s+1,&m),n=strlen(s+1),f[0].a[0][0]=1;
for(int i=0;i<m;i++)base.a[i][0]=1;
for(int i=1;i<m;i++)base.a[i-1][i]=1;
for(int i=0;i<10;i++){
d[0][i]=ksm(base,i);
for(int j=1;j<=n;j++)d[j][i]=ksm(d[j-1][i],10);
} for(int r=n;r;r--)
for(int l=r;l;l--)
if(l==r)c[l][r]=d[0][s[r]-'0'];
else c[l][r]=c[l+1][r]*d[r-l][s[l]-'0'];
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++)
f[i]=f[i]+f[j]*c[j+1][i];
cout<<f[n].a[0][0]<<endl;
return 0;
}