您的位置:首页 > 其它

[bzoj4870] [Shoi2017]组合数问题

2017-07-09 21:18 453 查看

题目大意

略…

分析

注意到一个重要的条件:t< k

那么根据组合数的意义,可以理解为从nk件物品里取若干件,满足取出物件数量模k等于t的方案数。

又发现k很小,然后可以设出DP:设f[i][j]表示前i件物品取的件数模k等于j的方案数。转移很显然。

用矩阵乘法可以做到O(k3lognk)

然而这个DP满足结合律,即假设我们已经求出了数组f
[],可以求出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
,g
;

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: