bzoj4037: [HAOI2015]数字串拆分(动态规划+矩阵)

www.cnblogs.com/shaokele/


bzoj4037: [HAOI2015]数字串拆分(动态规划+矩阵)##

  Time Limit: 10 Sec
  Memory Limit: 256 MB

Description###

  你有一个长度为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;
}
posted @ 2018-07-14 17:33  skl_win  阅读(408)  评论(0编辑  收藏  举报
Live2D