题目大意
略…
分析
注意到一个重要的条件:t< k
那么根据组合数的意义,可以理解为从nk件物品里取若干件,满足取出物件数量模k等于t的方案数。
又发现k很小,然后可以设出DP:设f[i][j]表示前i件物品取的件数模k等于j的方案数。转移很显然。
用矩阵乘法可以做到
O(k3lognk)
然而这个DP满足结合律,即假设我们已经求出了数组f[n][],可以求出f[2n][],列出的式子是一个卷积形式。于是可以暴力求这个卷积,同时用类似快速幂的方法求f[nk][]。时间复杂度
O(k2lognk)
可以开大数据范围用NTT加速到 O(klogklognk) (划
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=55;
typedef long long LL;
int n,mo,k,r,f[N],g[N];
void quick(LL x)
{
if (!x) return;
quick(x>>1);
memset(g,0,sizeof(g));
for (int i=0;i<k;i++)
{
for (int j=0;j<k;j++)
{
int t=(i+j)%k;
g[t]=(g[t]+(LL)f[i]*f[j])%mo;
}
}
memcpy(f,g,sizeof(f));
if (x&1)
{
memset(g,0,sizeof(g));
for (int i=0;i<k;i++)
{
g[i]=(g[i]+f[i])%mo;
g[(i+1)%k]=(g[(i+1)%k]+f[i])%mo;
}
memcpy(f,g,sizeof(f));
}
}
int main()
{
scanf("%d%d%d%d",&n,&mo,&k,&r);
f[0]=1;
quick((LL)n*k);
printf("%d\n",f[r]);
return 0;
}