卢卡斯定理证明

在开始介绍卢卡斯定理之前我们先介绍下面的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=218%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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章