数论 · Lucas 定理

前言

在 A 了 Lucas 模板之后,十几天才弄懂证明。

UPDATE

定理

组合数取模

对于整数 a ,   b ,   p a,\ b,\ p a, b, p p p p 为素数), a = ∑ i = 0 k a i p i ,   b = ∑ j = 0 k b j p j a=\sum_{i=0}^ka_ip^i,\ b=\sum_{j=0}^{k}b_jp^j a=i=0kaipi, b=j=0kbjpj

简而言之就是把 a ,   b a,\ b a, b 转化为 p p p 进制下的数。

( a b ) ≡ ( a k b k ) ∗ ( a k − 1 b k − 1 ) ⋯ ≡ ∏ i = 0 k ( a i b i ) ( m o d p ) \dbinom{a}{b} \equiv \dbinom{a_k}{b_k} * \dbinom{a_{k-1}}{b_{k-1}}\cdots \equiv \prod_{i=0}^k \dbinom{a_i}{b_i} \pmod p (ba)(bkak)(bk1ak1)i=0k(biai)(modp)

证明

先提出一个用二项式定理证的一个柿子 ( 1 + x ) p ≡ 1 + x p ( m o d p ) (1+x)^p \equiv 1+x^p \pmod p (1+x)p1+xp(modp)

都要看烂了,就不证了。

根据 a a a p p p 进制下拆分,我们有: ( 1 + x ) p = ( 1 + x ) a 0 ∗ ( ( 1 + x ) p ) a 1 ∗ ( ( 1 + x ) p 2 ) a 2 ∗ ⋯ ( ( 1 + x ) p k ) a k (1+x)^p=(1+x)^{a_0}* ((1+x)^{p})^{a_1}* ((1+x)^{p^2})^{a_2}* \cdots ((1+x)^{p^k})^{a_k} (1+x)p=(1+x)a0((1+x)p)a1((1+x)p2)a2((1+x)pk)ak

再结合最上面的柿子,就有 ( 1 + x ) p ≡ ( 1 + x ) a 0 ∗ ( 1 + x p ) a 1 ∗ ( 1 + x p 2 ) a 2 ∗ ⋯ ∗ ( 1 + x p k ) a k ( m o d p ) (1+x)^p \equiv (1+x)^{a_0}* (1+x^p)^{a_1}* (1+x^{p^2})^{a_2}* \cdots * (1+x^{p^k})^{a_k} \pmod p (1+x)p(1+x)a0(1+xp)a1(1+xp2)a2(1+xpk)ak(modp)

然后我们考虑左边的柿子,其中 x b x^b xb 次方的系数就是 C a b C_a^b Cab

b = ∑ j = 0 k b j p j b=\sum_{j=0}^{k}b_jp^j b=j=0kbjpj

所以在右边的每一个括号的次数 a k a_k ak 中,我们挑 b k b_k bk 次,再乘起来,就可以得到 x b x^b xb 项。对于第 k k k 个括号,就有 ( a k b k ) \dbinom{a_k}{b_k} (bkak) 中挑 b k b_k bk 次的方式。

这样就得到了 ( a b ) ≡ ( a 0 b 0 ) ∗ ( a 1 b 1 ) ∗ ⋯ ∗ ( a k b k ) ( m o d p ) \dbinom{a}{b} \equiv \dbinom{a_0}{b_0}* \dbinom{a_1}{b_1}* \cdots * \dbinom{a_k}{b_k} \pmod p (ba)(b0a0)(b1a1)(bkak)(modp)

按照这个柿子,我们就可以递归求解 ( a b ) %   p \dbinom{a}{b} \% \ p (ba)% p了。

证毕!

代码

其中, ( n m ) = n ! m ! ∗ ( n − m ) ! \dbinom{n}{m} = \dfrac {n!}{m!* (n-m)!} (mn)=m!(nm)!n!,这个可以预处理出每个数的阶乘再带入求值,除法直接用 逆元 即可。

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define rint register int
const int maxn = 1e5 + 5;
int T, n, m, p;
int a[maxn];

inline int read ()
{
	int x = 1, s = 0;
	char ch = getchar ();
	while (ch < '0' or ch > '9') {if (ch == '-') x = -1; ch = getchar ();}
	while (ch >= '0' and ch <= '9') s = s * 10 + ch - '0', ch = getchar ();
	return s * x;
}

inline int pow (int x, int t)
{
	int res = 1;
	x %= p;
	while (t >= 1)
	{
		if (t & 1) 
			res = res * x % p, t -= 1;
		t /= 2, x = x * x % p; 
	}
	return res;
}

inline int inv (int x)
{
	return pow (x, p - 2);
}

inline int cm (int n, int m)
{
	if (m > n) return 0;
	return ((a[n] * inv (a[m])) % p * inv (a[n - m]) % p);
}

inline int lucas (int a, int b)
{
	if (!b) return 1;
	return cm (a % p, b % p) * lucas (a / p, b / p) % p;
}

signed main ()
{
	a[0] = 1;
	T = read ();
	while (T--)
	{
		n = read (), m = read (), p = read ();
		for (rint i (1); i <= p; ++i) 
			a[i] = a[i - 1] * i % p;
		printf ("%lld\n", lucas (n + m, n));
	}
	return 0;
}

—— E n d End End——

posted @ 2022-03-25 07:25  pldzy  阅读(83)  评论(0)    收藏  举报