组合数求模
来源:互联网 发布:淘宝体检中心进不去 编辑:程序博客网 时间: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;}
阅读全文
0 0
- 组合数求模
- 组合数求模
- 组合数求模
- 组合数求模
- 组合数求模
- 组合数求模
- 组合数求模
- [知识精华]组合数求模
- 组合数求模总结
- LightOJ 1067 组合数求模
- FZU 2020 组合数求模
- 【数论】组合数求模
- 组合数求模模板
- 组合
- 组合
- 组合
- 组合
- 组合
- [HDU]6053 TrickGCD
- NOIP2017模拟赛(十四)总结
- ACM题集以及各种总结大全!
- Til the Cows Come Home
- Password
- 组合数求模
- OPNET LINK : fatal error LNK1181如何解决
- Fox And Two Dots
- hdu 2642 (summerIII J) 二维树状数组
- 【GRE】做题记录和总结
- 楼房重建
- PRML:二元变量分布
- Android 图片浏览功能简单实现(画廊效果实现,支持放大缩小)
- 通过Android Studio查看SDK源码