lucas定理 组合数前缀和模数求法

lucas定理用于求大组合数模小质数的值
公式 C(n, m) % p = C(n / p, m / p) * C(n%p, m%p) % p
如果n/p还是很大,就可以继续递归下去。
而C(n%p,m%p)可以直接根据C(n,m)=n!/(m!(n-m)!)来算;先存好12*…p的值然后算出(m!(n-m)!)对于p的逆元。就可以算出C(n,m)的值了
对于求小于5000以内的组合数模数可以直接用C(n,m)=C(n-1,m)+C(n-1,m-1)来存;
代码:

void CC(ll n,ll mod)
{
    for(int i=0;i<n;i++)
    {
        C[i][0]=C[i][i]=1;
    }
    for(int i=1;i<n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
            //sum[i][j]=C[i][j]+sum[i][j-1];
        }
    }
}在这里插入代码片

lucas实现代码
代码:

ll lucas(ll a,ll b){
    if(a<b)return 0;
    else if(b==0)return 1;
    else if(a<mod&&b<mod)return C[a][b];
    else return C[a%mod][b%mod]*lucas(a/mod,b/mod)%mod;
}在这里插入代码片

知道了大组合数求模公式后,可以进而得出求大组合数任意前缀和求模,
设num[n][m]=C(n,0)+C(n,1)+…+C(n,m);
可以先存小于n<mod&&m<mod的num值
代码:

    for(int i=0;i<mod;i++)
    {
        for(int j=0;j<mod;j++)
            sum[i][j]=(C[i][j]+sum[i][j-1])%mod;
    }在这里插入代码片

推导:
在这里插入图片描述

其中要结合lucas定理实现代码:
代码:

ll ca(ll n,ll m)
{
    if(n<mod) return sum[n][m];
    return (sum[n%mod][n%mod]%mod*ca(n/mod,m/mod-1)%mod+lucas(n/mod,m/mod)*sum[n%mod][m%mod])%mod;
}在这里插入代码片

以一道例题为例:
牛客acm训练题组
代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=2333;
ll C[2338][2338];
ll sum[2338][2338];
void CC()
{
    for(int i=0;i<mod;i++)
    {
        C[i][0]=C[i][i]=1;
    }
    for(int i=1;i<mod;i++)
    {
        for(int j=1;j<=i;j++)
        {
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
            //sum[i][j]=C[i][j]+sum[i][j-1];
        }
    }
    for(int i=0;i<mod;i++)
    {
        for(int j=0;j<mod;j++)
            sum[i][j]=(C[i][j]+sum[i][j-1])%mod;
    }
}
 
ll comp(ll a,ll b){
    return C[a][b];
}
 
ll lucas(ll a,ll b){
    if(a<b)return 0;
    else if(b==0)return 1;
    else if(a<mod&&b<mod)return C[a][b];
    else return C[a%mod][b%mod]*lucas(a/mod,b/mod)%mod;
}
ll ca(ll n,ll m)
{
    if(n<mod) return sum[n][m];
    return (sum[n%mod][n%mod]%mod*ca(n/mod,m/mod-1)%mod+lucas(n/mod,m/mod)*sum[n%mod][m%mod])%mod;
}
 
int main()
{
    ll a,b;
    int n;
    CC();
    scanf("%d",&n);
    while(n--){
        scanf("%lld%lld",&a,&b);
        ll sum=0;
       
        printf("%lld\n",ca(a,b));
    }
    return 0;
}在这里插入代码片

求特定组合数求模的办法

代码:

int Calc(int n,int m)
{
	return (LL)jc[n]*ny[m]%MOD*ny[n-m]%MOD;
}
for (int i=2;i<=30000;i++)
 jc[i]=(LL)jc[i-1]*i%MOD,ny[i]=(LL)(MOD-MOD/i)*ny[MOD%i]%MOD;
for (int i=2;i<=30000;i++) ny[i]=(LL)ny[i-1]*ny[i]%MOD;
在这里插入代码片
发布了109 篇原创文章 · 获赞 35 · 访问量 6017

猜你喜欢

转载自blog.csdn.net/weixin_43965698/article/details/95752410