就 是 要 我 们 从 n k 件 物 品 里 面 选 出 若 干 件,使 得 其 数 量 模 k 等 于 r 的 方 案 数 。
dp方程 f [ i , j ] 表示前 i 件物品拿了若干件使得其数量模 k 等 于 j 的 方 案 数。
非常明显的 i 与 i - 1递推的DP, 可以转化推矩阵,进行矩阵乘法。
那么显然有f [ i , j ] = f [ i − 1 ,j ] + f[ i − 1,j − 1 ] f [ i , j ]= f [ i − 1,j ]+ f [ i − 1, j − 1 ]
矩阵乘法优化即可,注意 k等于 1时 矩阵初始化 需要一直 ++而不是赋值为 1。
#include<bits/stdc++.h> using namespace std; #define maxn 55 #define ll long long ll n,mod,k,r; struct node { ll sx,sy; ll num[maxn][maxn]; void up1() { for(int i=0; i<sx; i++) for(int j=0; j<sy; j++) num[i][j]=0; } } A,ans; node operator*(node a,node b) { node c; c.sx=a.sx,c.sy=b.sy; c.up1(); for(int i=0; i<a.sx; i++) for(int j=0; j<b.sy; j++) for(int q=0; q<a.sy; q++) c.num[i][j]=(c.num[i][j]%mod+a.num[i][q]%mod*b.num[q][j]%mod)%mod; return c; } void qpow(ll b) { while(b) { if(b%2) ans=ans*A; A=A*A; b>>=1; } } int main() { scanf("%lld%lld%lld%lld",&n,&mod,&k,&r); ans.sx=1; ans.sy=A.sx=A.sy=k; A.up1(); ans.up1(); ans.num[0][0]=1; for(int i=0; i<k; i++) { A.num[i][i]=1; A.num[i][(i+1)%k]++; } qpow(n*k); printf("%lld\n",ans.num[0][r]); return 0; }