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

题意

这里写图片描述
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<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
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;i<k;i++)
        for (int j=0;j<k;j++)
            a.f[(i+j)%k]=(a.f[(i+j)%k]+b.f[i]*c.f[j]%p)%p;
}

void 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值