组合数取模
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 }
解决方案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 }
经典例题2:http://codeforces.com/gym/101775/problem/A
$1?≤?N?≤?10^9,3?≤?K?≤?10^5,mod=1000000007$
以上是关于组合数取模的主要内容,如果未能解决你的问题,请参考以下文章