组合数取模

Posted pavtlly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了组合数取模相关的知识,希望对你有一定的参考价值。

组合数取模问题为求$C_{n}^m % p$的值。根据$n$,$m$,$p$取值不同,方法不同。
在此之前我们先看些前置技能:

 

同余定理:$a≡b(mod\ m)$
性质:
1.传递性:若$a≡b(mod\ m)$,$b≡c(mod\ m)$,则$a≡c(mod\ m)$;
2.同余式相加:若$a≡b(mod\ m)$,$c≡d(mod\ m)$,则$a±c≡b±d(mod\ m)$;
3.同余式相乘:若$a≡b(mod\ m)$,$c≡d(mod\ m)$,则$ac≡bd(mod\ m)$。

 

逆元(数论倒数):对于正整数$a$和$m$,如果有$ax≡1(mod\ m)$,那么把这个同余方程中$x$的最小正整数解称为$a mod\ m$的逆元。
为什么叫数论倒数呢,因为没有$mod$操作,那么$x$就相当于$a$的倒数,有$mod$操作时效果上和倒数一样。同时我们把$a$的逆元写做:$inv(a)$。
逆元求解一般用扩展欧几里得算法,如果$m$为素数,那么还可以根据费马小定理得到逆元为$a^{m-2} mod\ m$。

扩展欧几里得算法和费马小定理求解逆元具有局限性,两种算法都要求a和m互素。

1.扩展欧几里得:$a*x + b*y = 1$
解$x$就是$a$关于$b$的逆元,解$y$就是$b$关于$a$的逆元
$a*x \% b + b*y \% b = 1 \% b$
$a*x \% b = 1 \% b$
$a*x = 1 (mod\ b)$

2.费马小定理:$a\hat{}(p-1)≡1 (mod\ p)$
两边同时除以$a$,$a\hat{}(p-2)≡inv(a)(mod\ p)$;即$inv(a)≡a^(p-2)(mod\ p)$

 

Lucas定理:

$n=n_kp^k+n_{k-1}p^{k-1}+...+n_1p+n_0$

$m=m_kp^k+m_{k-1}p^{k-1}+...+m_1p+m_0$

得到:$C_n^m=\prod\limits_{i=0}^{k}C_{ni}^{mi}(mod\ p)$

具体代码中实现:$Lucas(n,m,p)=C(n\%p,m\%p)*Lucas(n/p,m/p,p)\%p $

 

经典例题1:http://acm.fzu.edu.cn/problem.php?pid=2020

$1 <= m <= n <= 10^9, m <= 10^4, m < p < 10^9, p是素数$,$m$比较小,$n$,$p$比较大的情况。

该题cin输入会比较快,怀疑是scanf读入%lld比较耗时,或者是OJ问题。

解决方案1:Lucas定理

技术分享图片
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 typedef long long LL;
 7 
 8 LL fast_power(LL a,LL b,LL p){
 9     LL ans=1;
10     a%=p;
11     while(b){
12         if(b&1) ans=(ans*a)%p;
13         a=(a*a)%p;
14         b>>=1;
15     }
16     return ans;
17 }
18 
19 LL C(LL a,LL b,LL p){
20     if(b>a) return 0;
21     if(a==b) return 1;
22     LL ans1=1,ans2=1;
23     for(LL i=1;i<=b;i++){
24         ans1=ans1*(a-i+1)%p;
25         ans2=ans2*i%p;
26     }
27     return ans1*fast_power(ans2,p-2,p)%p;
28 }
29 
30 LL Lucas(LL a,LL b,LL p){
31     if(b==0) return 1;
32     return C(a%p,b%p,p)*Lucas(a/p,b/p,p)%p;
33 }
34 
35 int main(){
36     int t;
37     cin>>t;
38     while(t--){
39         LL n,m,p;
40         cin>>n>>m>>p;
41         cout<<Lucas(n,m,p)<<endl;
42     }
43     return 0;
44 }
View Code

解决方案2:暴力+逆元

技术分享图片
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 typedef long long LL;
 7 
 8 LL fast_power(LL x,LL n,LL mod){
 9     LL ans=1;
10     x%=mod;
11     while(n){
12         if(n&1) ans=(ans*x)%mod;
13         n>>=1;
14         x=(x*x)%mod;
15     }
16     return ans;
17 }
18 
19 int main(){
20     int t;
21     cin>>t;
22     while(t--){
23         LL n,m,p;
24         cin>>n>>m>>p;
25         LL ans1=n,ans2=1;
26         for(LL i=1;i<m;i++){
27             ans1=ans1*(n-i)%p;
28             ans2=ans2*i%p;
29         }
30         ans2=ans2*m%p;
31         cout<<ans1*fast_power(ans2,p-2,p)%p<<endl;
32     }
33     return 0;
34 }
View Code

 

经典例题2:http://codeforces.com/gym/101775/problem/A

$1?≤?N?≤?10^9,3?≤?K?≤?10^5,mod=1000000007$ 















以上是关于组合数取模的主要内容,如果未能解决你的问题,请参考以下文章

组合数取模(Lucas)

对组合数取模

组合数取模

排列组合+组合数取模 HDU 5894

组合数取模

组合数取模(转载)