介绍
这是个用来求解大组合数取模意义下的值的算法。
要求取模的数是个质数。
算法
卢卡斯定理就是这样一条柿子:
(
n
m
)
≡
(
n
m
o
d
p
m
m
o
d
p
)
(
n
÷
p
m
÷
p
)
(
m
o
d
p
)
{n\choose m}\equiv{n \bmod p \choose m \bmod p}{n\div p \choose m\div p} \pmod p
(mn)≡(mmodpnmodp)(m÷pn÷p)(modp)
(注意, ÷ \div ÷ 是整除。)
当我们有了这条结论之后,我们可以对 ( n ÷ p m ÷ p ) {n\div p \choose m\div p} (m÷pn÷p) 这部分不断使用卢卡斯定理,对 ( n m o d p m m o d p ) {n \bmod p \choose m \bmod p} (mmodpnmodp) 这一部分计算组合数。如果提前 O ( n ) O(n) O(n) 预处理阶乘以及阶乘的逆元,就可以做到 O ( l o g p n ) O(log_pn) O(logpn) 计算大组合数。
证明
显然
n
n
n 可以表示成
a
p
+
b
ap+b
ap+b,
m
m
m 可以表示为
c
p
+
d
cp+d
cp+d,那么上面的柿子可以变为:
(
a
p
+
b
c
p
+
d
)
≡
(
b
d
)
(
a
c
)
(
m
o
d
p
)
{ap+b \choose cp+d}\equiv{b \choose d} {a \choose c} \pmod p
(cp+dap+b)≡(db)(ca)(modp)
那么有:
(
1
+
x
)
n
≡
(
1
+
x
)
a
p
×
(
1
+
x
)
b
(
m
o
d
p
)
(
1
+
x
)
n
≡
(
(
1
+
x
)
p
)
a
×
(
1
+
x
)
b
(
m
o
d
p
)
\begin{aligned} (1+x)^n&\equiv(1+x)^{ap}\times (1+x)^b \pmod p\\ (1+x)^n&\equiv((1+x)^p)^a\times (1+x)^b \pmod p\\ \end{aligned}
(1+x)n(1+x)n≡(1+x)ap×(1+x)b(modp)≡((1+x)p)a×(1+x)b(modp)
我们考虑转化里面 ( 1 + x ) p (1+x)^p (1+x)p 这一项,用神奇的二项式定理
( 1 + x ) p ≡ ∑ i = 0 p ( p i ) x i ( m o d p ) (1+x)^p \equiv \sum_{i=0}^p {p\choose i} x^i \pmod p\\ (1+x)p≡i=0∑p(ip)xi(modp)
当 i = 0 i=0 i=0 时,这一项的值为 1 1 1。
当 i = p i=p i=p 时,这一项的值为 x p x^p xp。
当 i ∈ ( 0 , p ) i\in(0,p) i∈(0,p) 时, ( p i ) = p ! i ! ( p − i ) ! {p\choose i}=\frac {p!} {i!(p-i)!} (ip)=i!(p−i)!p! ,由于 p p p 是个质数,所以这个系数肯定是 p p p 的倍数,于是这一项为 0 0 0。
这里就是普通卢卡斯定理的限制—— p p p 必须是质数——的缘由,如果 p p p 不是质数,那么就不能保证这个系数是 p p p 的倍数。而如果 p p p 是个质数,那么当 i ∈ ( 0 , p ) i\in (0,p) i∈(0,p) 时,分母部分不可能含有 p p p 这个因子,而分子里肯定含有 p p p 这个因子,所以总的这个系数肯定是 p p p 的倍数。
所以有
∑
i
=
0
p
(
p
i
)
x
i
≡
1
+
x
p
(
m
o
d
p
)
\sum_{i=0}^p {p\choose i}x^i\equiv 1+x^p \pmod p
i=0∑p(ip)xi≡1+xp(modp)
带回去有:
(
1
+
x
)
n
≡
(
1
+
x
p
)
a
×
(
1
+
x
)
b
(
m
o
d
p
)
∑
i
=
0
n
(
n
i
)
x
i
≡
∑
i
=
0
a
(
a
i
)
x
i
p
×
∑
i
=
0
b
(
b
i
)
x
i
(
m
o
d
p
)
\begin{aligned} (1+x)^n&\equiv (1+x^p)^a\times (1+x)^b \pmod p\\ \sum_{i=0}^n {n\choose i}x^i&\equiv \sum_{i=0}^a {a\choose i} x^{ip} \times \sum_{i=0}^b {b\choose i} x^i \pmod p \end{aligned}
(1+x)ni=0∑n(in)xi≡(1+xp)a×(1+x)b(modp)≡i=0∑a(ia)xip×i=0∑b(ib)xi(modp)
左边这个多项式中, x c p + d x^{cp+d} xcp+d 的系数为 ( n c p + d ) {n\choose cp+d} (cp+dn),即 ( a p + b c p + d ) {ap+b\choose cp+d} (cp+dap+b)。
因为
b
<
p
b<p
b<p,所以右边只有
x
c
p
x^{cp}
xcp 和
x
d
x^d
xd 这两项的积为
x
c
p
+
d
x^{cp+d}
xcp+d,而这两项的系数之积为
(
a
c
)
(
b
d
)
{a\choose c}{b\choose d}
(ca)(db),所以有:
(
a
p
+
b
c
p
+
d
)
≡
(
b
d
)
(
a
c
)
(
m
o
d
p
)
{ap+b \choose cp+d}\equiv{b \choose d} {a \choose c} \pmod p
(cp+dap+b)≡(db)(ca)(modp)
得证。
AC代码:
#include <cstdio>
#include <cstring>
#define ll long long
int t,n,m,p;
ll fac[100010];
ll ksm(ll x,int y)
{
ll re=1,tot=x;
while(y)
{
if(y&1)re=re*tot%p;
tot=tot*tot%p;
y>>=1;
}
return re;
}
ll C(int x,int y)
{
if(x<y)return 0;
return fac[x]*ksm(fac[y]*fac[x-y]%p,p-2)%p;
}
ll lucas(ll x,ll y)
{
if(y==0)return 1ll;
return lucas(x/p,y/p)*C(x%p,y%p)%p;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d %d %d",&n,&m,&p);
fac[0]=1;
for(int i=1;i<=p;i++)
fac[i]=fac[i-1]*i%p;
printf("%lld\n",lucas(n+m,m));
}
}
瞎JB补充
英文名: l u c a s lucas lucas (好像有人叫 l u c a s s lucass lucass?)
卢卡斯定理还有另外一种形式:
对于
n
,
m
n,m
n,m 两个数,可以将他们表示为
p
p
p 进制下的数:
n
=
(
a
1
a
2
.
.
.
a
k
)
(
p
)
m
=
(
b
1
b
2
.
.
.
b
k
)
(
p
)
\begin{aligned} n&=(a_1a_2...a_k)_{(p)}\\ m&=(b_1b_2...b_k)_{(p)} \end{aligned}
nm=(a1a2...ak)(p)=(b1b2...bk)(p)
那么有:
C
n
m
=
C
a
1
b
1
×
C
a
2
b
2
×
.
.
.
×
C
a
k
b
k
C_n^m=C_{a_1}^{b_1} \times C_{a_2}^{b_2} \times ... \times C_{a_k}^{b_k}
Cnm=Ca1b1×Ca2b2×...×Cakbk
这种形式和上面那种其实是等价的,想想就明白了。
一道练习题。