bzoj4037: [HAOI2015]数字串拆分(动态规划+矩阵)
www.cnblogs.com/shaokele/
bzoj4037: [HAOI2015]数字串拆分(动态规划+矩阵)##
Time Limit: 10 Sec
Memory Limit: 256 MBDescription###
你有一个长度为n的数字串。定义f(S)为将S拆分成若干个1~m的数的和的方案数,比如m=2时,f(4)=5,分别为4=1+1+1+1你可以将这个数字串分割成若干个数字(允许前导0),将他们加起来,求f,并求和。比如g(123)=f(1+2+3)+f(1+23)+f(12+3)+f(123)。已知字符串和m后求答案对998244353(7×17×223+1,一个质数)取模后的值。
Input###
第一行输入一个字符串,第二行输入m
Output###
仅输出一个数表示答案
Sample Input 1###
123
3
Sample Output 1###
394608467
HINT###
对于100%的数据,字符串长度不超过500,m<=5
题目地址: bzoj4037: [HAOI2015]数字串拆分
题目大意:
定义 \(f(x)\) 为把 \(x\) 有序拆分成若干 \(1\) 到 \(m\) 之间数的方案数。
例如当 m=2 时,4=1+1+1+1=1+1+2=1+2+1=2+1+1=2+2,因此 \(f(x)=5\)
给定一个 n 位数字串 \(S\) 将这个数字串分割成若干数字(允许前导零),将它们加起来,求 \(f\) ,再对所有方案求和并输出。
例如 123 的答案 g(123)=f(1+2+3)+f(12+3)+f(1+23)+f(123)。
题解:
$$ f(x)=\sum_{i=1}^{m} f(x-i)$$
发现 m 非常小,可以用矩阵乘法快速求出 f(x) **
对于最终答案,我们考虑 dp ,g(i) 表示前 i 位的答案,由于 f 中还有加法,我们不能转移。**
假设矩阵为 A ,f(x) 和 \(A^x\) 是等价的。 \(f(a+b)=A^{a+b}=A^aA^b\) ,也就是说我们可以化加为乘
在求 g 时,保留矩阵形式,那么就有转移:
$$ g(i)=\sum_{j=1}{i-1}g(j)*A。$$
AC代码
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
const int N=1005,Mod=998244353;
int n,m,ch[N];
char st[N];
struct matrix{
int A[6][6];
}f[15][N],g[N],tran;
matrix operator *(matrix a,matrix b){
matrix res;
memset(res.A,0,sizeof(res.A));
for(int i=1;i<=m;i++)
for(int k=1;k<=m;k++)
if(a.A[i][k])
for(int j=1;j<=m;j++){
res.A[i][j]=(res.A[i][j]+(ll)a.A[i][k]*b.A[k][j]%Mod);
if(res.A[i][j]>=Mod)res.A[i][j]-=Mod;
}
return res;
}
matrix operator +(matrix a,matrix b){
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++){
a.A[i][j]+=b.A[i][j];
if(a.A[i][j]>=Mod)a.A[i][j]-=Mod;
}
return a;
}
matrix fast_pow(matrix x,int e){
matrix res=x;
while(e){
if(e&1)res=res*x;
x=x*x;
e>>=1;
}
return res;
}
int main(){
scanf("%s",st+1);
n=strlen(st+1);
scanf("%d",&m);
for(int i=1;i<=n;i++)ch[i]=st[i]-'0';
for(int i=1;i<=m;i++){
tran.A[i][m]=f[0][1].A[i][i]=1;
if(i>1)tran.A[i][i-1]=1;
}
for(int i=1;i<=9;i++){
f[i][1]=f[i-1][1]*tran;
for(int j=2;j<=n;j++)
f[i][j]=fast_pow(f[i][j-1],9);
}
g[0].A[1][m]=1;matrix tmp;
for(int i=1;i<=n;i++){
tmp=f[ch[i]][1];
for(int j=i-1;j>=0;j--){
g[i]=g[i]+g[j]*tmp;
if (j && ch[j])tmp=tmp*f[ch[j]][i-j+1];
}
}
printf("%d\n",g[n].A[1][m]);
return 0;
}