组合数求模

来源:互联网 发布:淘宝体检中心进不去 编辑:程序博客网 时间:2024/05/29 10:12

开心,今天搞明白了组合数求模是怎么玩的

先来说说组合数求模吧

一、小范围数据

一般的,当数据范围比较小的时候,比方说只有1e4的范围,我们可以直接暴力求解,使用Pascal公式将C(n,k)直接求出来存放入数组中,Pascal公式为C(n,k)=C(n-1,k)+C(n-1,k-1)

代码如下

#include<iostream>#include<algorithm>using namespace std;const int mod=1e9+7;const int maxn=2020;int c_mod[maxn*2][maxn*2];void set_c_mod(){    c_mod[0][0]=1;    for(int i=1;i<maxn*2;i++)    {        c_mod[i][0]=c_mod[i][i]=1;        for(int j=1;j<i;j++)            c_mod[i][j]=(c_mod[i-1][j]+c_mod[i-1][j-1])%mod;    }}int main(){    int n,k;    set_c_mod();    while(cin>>n>>k)        cout<<c_mod[n][k]<<endl;    return 0;}


二、中等范围数据

在数据范围比较大,而模数又比数据最大值大的时候,我们可以采用求解乘法逆元的方法来解决组合数求模的问题

在模运算中,加,减,乘的运算规则都与基础四则运算类似

即可以表示为

(a*b)%m=((a%m)*(b%m))%m

(a+b)%m=(a%m+b%m)%m

(a-b)%m=(a%m-b%m)%m

但是对于除法是没有这样的性质的,对于除法而言,(a/b)%mod != ( (a%mod) / (b%mod) )%mod
但是,有一个东西叫做乘法逆远,我们可以利用这个东西来进行除法的取模运算
逆元定义:对整数a,满足ax≡1(modm)的解x称为a关于模m的逆元素

求解逆元可以通过扩展欧几里得定理计算,在模数m为质数时,我们也可以通过费马小定理求解

对于(a/b)%m,就可以通过以下方法计算了

(a/b)%m=(a*(b的逆远))%m

而C(n,k)=n!/( (n-1)! * k! )

所以可以先计算(n-k)!的模m的逆元记为x1,再计算k!模m的逆元记为x2

那么C(n,k)%m=(((n!%m)*x1%m)*x2)%m


这一方法的步骤为
1、使用数组 jc_mod 记录0!~n!模m的值

2、计算jc_mod[k]与jc_mod[n-k]模m的逆元分别记为x1,x2

3、计算((jc_mod[n]*x1)%m*x2)%m即为C(n,k)%m的结果


代码为,我求逆元用的是费马小定理
#include<iostream>#include<algorithm>using namespace std;typedef long long ll;const int maxn=2e6+10;const int mod=1e9+7;ll jc_mod[maxn];void set_mod(){    jc_mod[0]=1;    for(ll i=1;i<maxn;i++)        jc_mod[i]=jc_mod[i-1]*i%mod;}ll quick_mod(ll a,ll b,ll m){    ll ans=1;    while(b)    {        if(b&1) ans=ans*a%m;        b>>=1;        a=a*a%m;    }    return ans;}ll c_mod(ll n,ll k){    ll x1=quick_mod(jc_mod[k],mod-2,mod);    ll x2=quick_mod(jc_mod[n-k],mod-2,mod);    return ((jc_mod[n]*x1%mod)*x2)%mod;}int main(){    ll n,k;    set_mod();    while(cin>>n>>k)        cout<<c_mod(n,k)<<endl;    return 0;}


、数据范围极大
当数据范围极大的时候可以使用卢卡斯定理
卢卡斯定理表达式为 C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p
代码为
#include<iostream>#include<algorithm>using namespace std;typedef unsigned long long ll;const ll mod=10007;const ll maxn=1e9;const ll maxj=10010;ll jc_mod[maxj];void set_jc_mod(){    jc_mod[0]=1;    for(ll i=1;i<maxj;i++)        jc_mod[i]=i*jc_mod[i-1]%mod;}ll quick_mod(ll a,ll b,ll p){    ll ans=1;    while(b)    {        if(b&1) ans=(ans*(a%p))%p;        a=((a%p)*(a%p))%p;        b>>=1;    }    return ans;}ll c_mod(ll n,ll k){    ll x1=quick_mod(jc_mod[k],mod-2,mod);    ll x2=quick_mod(jc_mod[n-k],mod-2,mod);    return ((jc_mod[n]*x1%mod)*x2)%mod;}ll lucas(ll n,ll k){    if(n<mod&&k<mod) return c_mod(n,k);    else return c_mod(n%mod,k%mod)*lucas(n/mod,k/mod)%mod;}int main(){    int n,k;    while(cin>>n>>k)        cout<<lucas(n,k)<<endl;}



原创粉丝点击