在開始介紹盧卡斯定理之前我們先介紹下面的3個定理
1.乘法逆元
如果ax≡1 (mod p),且gcd(a,p)=1(a與p互質),則稱a關於模p的乘法逆元爲 x
ax≡1 (mod p) 這個等式用中文描述就是 a乘一個數x並模p等於1,即 a%p*x%p=res,res%p=1
2.費馬小定理:如果p是一個質數,而整數a不是p的倍數,即這兩個數互質,則有
a^(p - 1) ≡ 1 mod(p) gcd(a, p) = 1
3.拓展歐幾里得
已知整數a、b,擴展歐幾里得算法可以在求得a、b的最大公約數的同時,
能找到整數x、y(其中一個很可能是負數),使它們滿足貝祖等式ax + by = gcd(a, b)
problem_1:爲什麼我們可以用費馬小定理來求得逆元?
證明: 由費馬小定理 a^(p - 1) ≡ 1 mod(p) 變形得 ->> a * a^(p - 2) ≡ 1 mod(p)
因爲 a, p 互質,a * a^(p - 2) ≡ 1 mod(p) 且 ax≡1 (mod p) 則 x = a^(p - 2) mod(p)
problem_2:爲什麼我們可以用拓展歐幾里得來求得逆元?
證明:法1 我們都知道模後的數就是餘數,比如12%5=12-5*2=2,18%4=18-4*4=2。(/是程序運算中的除)
那麼ax≡1 (mod p)即ax-yp=1.把y寫成+的形式就是ax+py=1,爲方便理解下面我們把p寫成b就是ax+by=1。
就表示x是a的模b乘法逆元,y是b的模a乘法逆元。然後就可以用擴展歐幾里得求了
法2 那麼怎麼由擴展歐幾里得算法求逆元呢?
很簡單~
使用條件: a,b爲正整數,而且gcd(a,b) = 1
證明:
因爲a,b 互質,所以一定有 ax+by = 1
兩邊同時對b 取餘
ax%b + by %b = 1%b -------> ax%b = 1%b
即 ax ≡ 1 (mod b)
擴展歐幾里得中x 就是a關於b的逆元
同理y 就是 b 關於 a的逆元
所以使用完歐幾里得算法,我們判斷返回值d 是否爲1,爲1說明 gcd(a,b) = 1
即求得的x就是 a關於b的逆元
problem_3:這個逆元有啥子用?
乘法逆元最大的作用就是,在要除以一個數,再取模時,把除法變成乘法運算,然後再取模。
因爲除法,比如用16/5應該是3.2,但是計算機會算成3.。。誤差有沒有,用double就更不用說了,
數大了一定有誤差,所以,有了逆元!!!!
(a / b) % p = (a * inv(b) ) % p = (a % p * inv(b) % p) % p // inv代表的是逆元
例如:12 / 5 mod 7 = ?
gcd(g, 7) = 1------->>>>>>> inv(v) = 3
----->>>>>> (12 * 3) % 7 = 1
好了,在瞭解了上面的知識後,我們進入正題,看一看這個盧卡斯到底什麼東西.
定理描述:
k
C(n, m) = ∏ C(ni, mi) (mod p)
i=0
Lucas定理是用來求 c(n,m) mod p,p爲素數的值。
證明: C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p
C(n, m) mod p = n!/(m!(n - m)!) mod p
當我們要求(a/b)mod p的值,且b很大,無法直接求得a/b的值時,
我們可以轉而使用乘法逆元k,將a乘上k再模p,即(a*k) mod p。 其結果與(a/b) mod p等價。
也就是Lucas(n,m)%p=Lucas(n/p,m/p)*C(n%p,m%p)%p
注意:C(N, 0, P) = 1
例題:洛谷: 3807.
Ac 代碼:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
ll fac[maxn];
ll n, m, mod;
void init()
{
fac[0] = 1;
for(int i = 1; i <= mod; ++i)
fac[i] = (i * fac[i - 1]) % mod; // 先把mod內的階乘算出來, 進行預處理
}
ll kpow(ll a, ll b) // 快速冪
{
ll ans = 1;
while(b){
if( b & 1) ans = (ans * a) % mod;
a = (a * a) % mod;
b>>=1;
}
return ans;
}
ll inv(ll x) // 求逆元
{
return kpow(x, mod - 2);
}
ll C(ll n, ll m) //
{
if(n < m) return 0;
return (fac[n] * inv(fac[m] * fac[n - m])) % mod;
}
ll Lucas(ll k, ll b) // 一直進行遞歸, 直到 進行到這裏 -->>> C(N, 0, P) = 1
{
if(b == 0) return 1;
return C(k%mod, b%mod) * Lucas(k/mod, b/mod)% mod;
}
int main(void)
{
int t;
cin>>t;
while(t--){
scanf("%lld%lld%lld", &n, &m, &mod);
init();
printf("%lld\n", Lucas(n, m));
}
return 0;
}