• 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{S[j+1,i]}。$$
      


    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
    ",g[n].A[1][m]);
    	return 0;
    }
    
  • 相关阅读:
    任务18格式化
    任务17分区
    任务16 BIOS与CMOS
    任务15硬件组装过程说明
    任务14选配机箱
    任务13选配电源
    任务12选配显卡
    任务11选配机械硬盘
    任务10选配固态硬盘
    Android自定义控件:动画类(八)----ObjectAnimator基本使用
  • 原文地址:https://www.cnblogs.com/shaokele/p/9310309.html
Copyright © 2020-2023  润新知