题目大意:给你b个格子,每个格子有n个数(1到9),你顺序从b个格子中各取一个数,依次排列好,组成一个b位数,求使得这个b位数%x余k的方案数(同一格子内相同的数字算不同方案)。
DP题,状态f[i][j]表示前i个格子取出的数%x余j的方案数。f[i][j]到f[i+1][m]转移条件是(j*10+q)%x==m,q为第i+1个格子选的数。化简之后变成F[i]*A[]=F[i+1],F和A为矩阵,这个题就变成了矩阵乘法,然后由于b太大,用快速幂优化,最后输出余k的答案即可。
难点在于A矩阵的构造,想不通的可以看一下我代码。。。虽然丑但是可读性应该是还阔以的。
快速幂一定要写非递归的。。。不然一万的long long会爆栈。。。mod注意点就好。
第一次做出来e题,也是有点小兴奋。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
using namespace std;
int n,b,k,x;
int a[55000];
int bb[110];
long long modd;
struct MATRIX{
long long m[110][110];
MATRIX friend operator*(MATRIX a,MATRIX b){
MATRIX c;
memset(c.m,0,sizeof(c));
for(int i=0;i<x;i++){
for(int j=0;j<x;j++){
for(int k=0;k<x;k++){
c.m[i][j]+=(a.m[i][k]*b.m[k][j]);
c.m[i][j]%=modd;
}
}
}
return c;
}
}q,ans,c,dui[110];
void PR(MATRIX a){
printf("\n");
for(int i=0;i<x;i++){
for(int j=0;j<x;j++)printf("%d ",a.m[i][j]);
printf("\n");
}
printf("\n");
}
int main(){
modd=1000000000+7;
scanf("%d%d%d%d",&n,&b,&k,&x);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)bb[a[i]%x]++;
for(int i=0;i<x;i++){
for(int j=0;j<x;j++){
q.m[i][(i*10+j)%x]=bb[j%x];
q.m[i][(i*10+j)%x]%=modd;
}
}
// PR(q);
// q=KUAISUMI(q,b-1);
int tail=1;
int w=b-1;
dui[1]=q;
while(w!=1&&w!=0){
if(w&1){
tail++;
dui[tail]=dui[tail-1]*dui[tail-1];
}else{
dui[tail]=dui[tail]*dui[tail];
}
w>>=1;
}
while(tail!=1){
dui[tail-1]=dui[tail-1]*dui[tail];
tail--;
}
q=dui[1];
for(int i=0;i<x;i++)ans.m[0][i]=bb[i];
// PR(ans);
if(b!=1)ans=ans*q;
cout<<ans.m[0][k]<<endl;
return 0;
}
//f[i][j][k] 前i个箱子中,第i个箱子拿第j个数余k的数量
//j<=1..n f[i][k*10+a[j]]+=f[i-1][k]
//f[i][0..x-1]=f[i-1][0..x-1]*A[0..x-1][0..x-1]
本文介绍了一种使用动态规划(DP)算法解决组合数学问题的方法,具体涉及到从多个元素集合中选择元素并组成特定模式的过程。通过构建状态转移方程和矩阵运算,有效地解决了求解特定条件下方案数量的问题。
354

被折叠的 条评论
为什么被折叠?



