乘法逆元&lucas定理
乘法逆元
乘法逆元性质
若整数b,m互质,并且对于任意的整数 a,如果满足 b∣a,则存在一个整数 x,使得 a/b≡a×x(modm),则称 x 为 b 的模 m 乘法逆元,记为b−1(modm)。b 存在乘法逆元的充要条件是 b 与模数 m 互质。当模数 m 为质数时,bm−2 即为 b 的乘法逆元。
乘法逆元证明
设inv(x)为b的逆元且n为质数
即a/b≡a∗inv(b)(modn)
a ≡ a * b * inv(b) (mod n)$
b*inv(b) ≡ 1 (mod n)$
由费马小定理可得bn−1≡1(modn)
所以b∗bn−2≡1(modn)
故n为质数时,inv(b)=bn−2
很显然如果a是n的倍数则逆元不存在
代码
#include <iostream>
using namespace std;
#define ll long long
ll t;
ll qim(ll a, ll b, ll m){
ll res = 1;
while(b){
if(b & 1){
res *= a;
res %= m;
}
a *= a;
a %= m;
b >>= 1;
}
return res;
}
int main(){
cin >> t;
while(t --){
ll a, b; cin >> a >> b;
ll res = qim(a, b - 2, b);
if(a % b) cout << res << endl;
else cout << "impossible" << endl;
}
return 0;
}
lucas定理
lucas定理的作用
lucas定理是一个与组合数有关的数论定理,根据组合定义Cmn=m!/n!(m−n)!=m∗(m−1)∗⋅⋅⋅∗(m−n+1)/n!如果当m>30很明显数值无法用longlong承担,不过我们可以通过乘法逆元将除法转换为乘法Cmn≡m!∗inv(n!)∗inv[(m−n)!](modP),虽然可以通过这种方式解决问题,但有时候质数P<m!,这样就无法保证n!与m−n!的逆元是否存在了,因为有可能是P的倍数,这时候就要使用lucas定理
lucas定理
对于非负整数 n,m和质数 P,Cmn≡∏i=0kCmini(modP) ,其中m=mkpk+⋅⋅⋅+m1p+m0,n=nkpk+⋅⋅⋅+n1p+n0 是m 和n 的p进制展开,式子也可得Cmn=Cm%pn%p⋅C⌊m/p⌋⌊n/p⌋(modp)
lucas定理证明
前置知识:
二项式公式(a+b)n=Cn0bn+Cn1a1⋅bn−1+⋯+Cnnan=∑k=0nCnkakbn−k
式子一:Cpx≡0(modp),0<x<p 其中p为质数
由于 0<x<p,并且 p 是质数,所以Cpx=p!/x!∗(p−x)!=p∗(p−1)!/x∗(x−1)!∗(p−x)!=p/x∗Cp−1x−1
因为 x<p,所以p/x存在x膜p意义下的逆元inv(x)
Cpx≡p∗inv(x)∗Cp−1x−1(modp)
显然右边为膜数p的倍数,所以Cpx≡0(modp)
式子二:(1+x)p≡1+xp(modp)
由二项式展开可得:(1+x)p=Cp0xp+Cp1⋅xp−1+⋯+Cpp=∑k=0pCpkxk
将式子一代入可得:(1+x)p≡1+xp(modp)
lucas定理证明: Cmn=Cm%pn%p⋅C⌊m/p⌋⌊n/p⌋(modp)
设{⌊m/p⌋=qm⌊n/p⌋=qn,{m%p=rmn%p=rn,{m=qmp+rmn=qnp+rn
再由二项式定理(1+x)m=∑k=0mCmkxk
可得(1+x)m=(1+x)qmp+rm=(1+x)qmp∗(1+x)rm=[(1+x)p]qm∗(1+x)rm≡(1+xp)qm∗(1+x)rm=∑i=0qmCqmixpi∗∑j=0rmCrmjxj=∑i=0qm∑j=0rmCqmiCrmjxpi+j(modp)
之后归纳总结出xip+j的系数k=ip+j
得:(1+x)m≡∑i=0mCqm⌊i/p⌋Crm⌊n%p⌋xi(modp)
∑k=0mCmkxk≡∑i=0mCqm⌊i/p⌋Crm⌊n%p⌋xi(modp)
∑k=0mCmk≡∑i=0mCqm⌊i/p⌋Crm⌊n%p⌋(modp)
对比系数,且k=n,则Cmn≡Crmn%p⋅Cqm⌊n/p⌋≡Cmn=Cm%pn%p⋅C⌊m/p⌋⌊n/p⌋(modp) 得证
代码
#include <iostream>
using namespace std;
#define ll long long
ll t;
ll a, b, p;
ll qim(ll a, ll b, ll p){
ll res = 1;
while(b){
if(b & 1){
res *= a;
res %= p;
}
a *= a;
a %= p;
b >>= 1;
}
return res;
}
ll C(ll a, ll b){
ll res = 1;
for(ll i = 1, j = a; i <= b; j --, i ++){
res = (j * res) % p;
res = (res * qim(i, p - 2, p)) % p;
}
return res;
}
ll lucas(ll a, ll b, ll p){
if(a < p && b < p ) return C(a, b);
return C(a % p, b % p) * lucas(a / p, b / p, p) % p;
}
int main(){
cin >> t;
while(t --){
cin >> a >> b >> p;
cout << lucas(a, b, p) << endl;
}
return 0;
}