cf#341-E. Wet Shark and Blocks --DP+矩阵快速幂

给出n,b,k,x;  以及n个数   n<=1e5,b<=1e9,k,x<=100

给你一个盒子,盒子里面有n个数,全是1-9,

让你从盒子1,选一个数,盒子2选一个.....一共b个盒子,

这些盒子选出来的每一种方案都对应着一个数NUM,求num%x==k的方案数,对1e9+7取模



//设num=k*x+j,num%x=j, (10*num+d)%x=(10k*x+10j+d)%x=(10j+d)%x;

即知道前i-1个盒子时 余数为j的方案数,就能知道第i个盒子时,余数为(10j+d)%x的方案数的一个增量

 
//状态转移方程为 dp[i][(10*j+d)%x]+= dp[i-1][j]*(num[d]);
//显然,最终答案是 dp[b][k] , b太大,并且每次转移的操作是一样的,所以需要用矩阵快速幂优化
//我们把前i-1个数下的余数d为0~x-1的方案数写成一行
//每个状态i显然是一个x*1的矩阵,我们需要构造一个x*x的矩阵
//在前i-1个数时,余数为d的方案下,只要(d*10+num)%x== Dj,就表示这个d与num结合后,会对前i个数下的余数为Dj的情况有贡献
//那么就在系数矩阵的第Dj 列的  d行位置 加上num[1~9],从而构造出系数矩阵 :

for (i=0;i<x;i++)
	{
		for (j=1;j<=9;j++)
		p.mat[i][(10*i+j)%x]+=num[j];//上一状态下余数为i的方案数,乘上num[j],便可以得到下一状态余数为(10*i+j)%x的方案数增量
	}

初始化 条件为 初始矩阵DP的【0】【0】=1;  // 余数为0的方案为1;


#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;

struct Matrix
{
    __int64 mat[105][105];
};

Matrix unit_matrix,p,dp ;
__int64 MAX=100;
__int64 mod=1e9+7;
Matrix mul(Matrix a, Matrix b) //矩阵相乘
{

    Matrix res;
    for(int i = 0; i < MAX; i++)
        for(int j = 0; j < MAX; j++)
        {
            res.mat[i][j] = 0;
            for(int t = 0; t < MAX; t++)
            {
                res.mat[i][j] += a.mat[i][t] * b.mat[t][j];
                res.mat[i][j] %= mod;
            }
        }
		return res;
}

Matrix pow_matrix(Matrix a, __int64 m)  //矩阵快速幂
{
    Matrix res = unit_matrix;
    while(m != 0)
    {
        if(m & 1)
            res = mul(res, a);
        a = mul(a, a);
        m >>= 1;
    }
    return res;
}
__int64 num[10];
int main()

{
    __int64  i, j, t;
	__int64 n,b,x,k;
	scanf("%d%d%d%d",&n,&b,&k,&x);
	__int64 xx;
	for(i = 1; i<=n; i++)
	{
		scanf("%d",&xx);
        num[xx]++;
	}
	//初始化单位矩阵
 	for(i = 0; i < x; i++) unit_matrix.mat[i][i] = 1;

	for (i=0;i<x;i++)
	{
		for (j=1;j<=9;j++)
		p.mat[i][(10*i+j)%x]+=num[j];//上一状态下余数为i的方案数,乘上num[j],便可以得到下一状态余数为(10*i+j)%x的方案数增量
	}
	dp.mat[0][0]=1;
	dp=mul(dp,pow_matrix(p,b));
	printf("%I64d\n",dp.mat[0][k]);


return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值