给出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的方案数增量 }
#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; }