代码和注释
#include<cstdio> #include<iostream> #include<cstring> using namespace std; const int mi[]{1,2,4,8,16,32}; const int MO=1000000007; #define ll long long ll n,m,k,ans,f[32],a[32][32],aa[32][32],tmp[32],tmp1[32][32]; bool can[32]; void mul(ll rst[32],ll a[32][32]){ memset(tmp,0,sizeof(tmp)); for(int i=0;i<mi[m];i++) for(int j=0;j<mi[m];j++) tmp[i]=(tmp[i]+rst[j]*a[j][i])%MO; for(int i=0;i<mi[m];i++)rst[i]=tmp[i]; } void mul2(ll rst[32][32],ll a[32][32]){ memset(tmp1,0,sizeof(tmp1)); for(int row=0;row<mi[m];row++) for(int i=0;i<mi[m];i++) for(int j=0;j<mi[m];j++){//矩阵乘法 tmp1矩阵第row行第i列位置上的数等于rst矩阵第row行上第j个数与a矩阵第i列上第j个数对应相乘后 tmp1[row][i]=(tmp1[row][i]+rst[row][j]*a[j][i])%MO;//所有mi[m]个乘积的和 } for(int i=0;i<mi[m];i++) for(int j=0;j<mi[m];j++) rst[i][j]=tmp1[i][j];// } int main(){ scanf("%lld%lld%lld",&n,&m,&k);//%lld 卡过一次 for(int i=0;i<mi[m];i++){ int ti=i,cnt=0; while(ti){ if(ti%2)cnt++; ti/=2; }//cnt为 ti中1的个数 if(m-cnt<=k)can[i]=true;//i的二进制表示的方案合法 }//找出每个状态的所有的方案中合法的 for(int j=0;j<mi[m];j++) if(can[j]){//若j这个方案可以 memset(f,0,sizeof(f));f[j]=1;//can[j]==1时 f[j]=1 memset(a,0,sizeof(a)); for(int i=0;i<mi[m];i++)if(can[i]){ a[i/2][i]=1; //i的二进制去掉最右一位,左边加一位0 得到的二进制 能通过转移得到i a[i/2 + mi[m-1]][i]=1; //i的二进制去掉最右一位,左边加一位1 得到的二进制 能通过转移得到i } memset(aa,0,sizeof(aa)); for(int i=0;i<mi[m];i++)aa[i][i]=1; //单位矩阵 任何矩阵乘以该矩阵 都等于原矩阵 (矩阵的初始化) ll tn=n; while(tn){ if(tn%2)mul2(aa,a);//经过此句,aa=a mul2(a,a); tn/=2; } //矩阵乘法的快速幂 mul(f,aa); //aa中存了构造的矩阵 ans=(ans+f[j])%MO; } printf("%lld",ans); return 0; }