bzoj 4870: [Shoi2017]组合数问题 动态规划

题意

bzoj 4870: [Shoi2017]组合数问题 动态规划_第1张图片
1 ≤ n ≤ 10^9, 0 ≤ r < k ≤ 50, 2 ≤ p ≤ 2^30 − 1

分析

拿到题目就开始狂推式子,看了题解才发现原来是dp。
我们从直观上来理解我们要求的这个诡异的式子。
实际上就是要我们从 nk 件物品里面选出若干件,使得其数量模k等于r的方案数。
显然的dp方程 f[i,j] 表示前i件物品拿了若干件使得其数量模k等于j的方案数。
那么显然有 f[i,j]=f[i1,j]+f[i1,j1]
矩阵乘法优化即可。
复杂度 O(k3logn)

还有一种更棒的做法,同样是dp,但可以发现 f[n2,i+j]+=f[n,i]f[n,j]
可以理解成枚举前n个物品的选法和后n个物品的选法。
那么直接对dp数组做快速幂即可。
复杂度 O(k2logn)

代码

#include
#include
#include
#include
#include
using namespace std;

typedef long long LL;

const int N=55;

LL n,p,k,r;
struct arr{LL f[N];}ans,a;

void mul(arr &a,arr b,arr c)
{
    memset(a.f,0,sizeof(a.f));
    for (int i=0;ifor (int j=0;jvoid ksm(arr x,LL y)
{
    y--;
    while (y)
    {
        if (y&1) mul(ans,ans,x);
        mul(x,x,x);y>>=1;
    }
}

int main()
{
    scanf("%lld%lld%lld%lld",&n,&p,&k,&r);
    a.f[0]++;a.f[1%k]++;ans=a;
    ksm(a,n*k);
    printf("%lld",ans.f[r]);
    return 0;
}

你可能感兴趣的:(动态规划)