给定
n
,
m
,
p
n,m,p
n,m,p,其中
n
,
m
n,m
n,m 较大,
p
p
p 为质数且不是很大,求
(
n
m
)
m
o
d
p
\dbinom{n}{m}\bmod p
(mn)modp
L
u
c
a
s
\rm Lucas
Lucas 定理
(
n
m
)
≡
(
⌊
n
p
⌋
⌊
m
p
⌋
)
⋅
(
n
m
o
d
p
m
m
o
d
p
)
(
m
o
d
p
)
(
p
为
质
数
)
\dbinom{n}{m}\equiv\dbinom{\left\lfloor\frac{n}{p}\right\rfloor}{\left\lfloor\frac{m}{p}\right\rfloor}\cdot \dbinom{n\bmod p}{m\bmod p}\pmod p(p 为质数)
(mn)≡(⌊pm⌋⌊pn⌋)⋅(mmodpnmodp)(modp)(p为质数)
引理 1 1 1:
观察
( p n ) ≡ p ! ( p − n ) ! ⋅ n ! ( m o d p ) \dbinom{p}{n}\equiv\dfrac{p!}{(p-n)!\cdot n!}\pmod p (np)≡(p−n)!⋅n!p!(modp)
分子含有质因数 p p p,所以当分母不含质因数 p p p 时答案为 0 0 0。否则,仅当 n = 0 n=0 n=0 或 n = p n=p n=p 时分母也含有质因数 p p p,答案为 p ! p ! ⋅ 0 ! = 1 \dfrac{p!}{p!\cdot 0!}=1 p!⋅0!p!=1。
引理 2 2 2:
考虑这样一个式子
( a + b ) p (a+b)^p (a+b)p
根据二项式定理,这个就是
∑ i = 0 p ( p i ) a i b p − i \sum\limits_{i=0}^{p}\dbinom{p}{i}a^i b^{p-i} i=0∑p(ip)aibp−i
由引理 1 1 1,只有 i = 0 i=0 i=0 或 i = p i=p i=p 时 ( p i ) \dbinom{p}{i} (ip) 在模 p p p 的意义下值为 1 1 1,所以( a + b ) p ≡ a 0 b p + a p b 0 ≡ a p + b p ( m o d p ) \begin{aligned} (a+b)^p &\equiv a^0 b^p+a^p b^0\\ &\equiv a^p+b^p\pmod p \end{aligned} (a+b)p≡a0bp+apb0≡ap+bp(modp)
那么
( a x + b y ) p ≡ a p x p + b p y p ( m o d p ) (ax+by)^p\equiv a^p x^p+b^p y^p\pmod p (ax+by)p≡apxp+bpyp(modp)
由费马小定理得a p x p + b p y p ≡ a x p + b y p ( m o d p ) a^p x^p+b^p y^p\equiv a x^p+b y^p\pmod p apxp+bpyp≡axp+byp(modp)
就是说可以直接把指数 p p p 往里乘。
证明:
( x + 1 ) n (x+1)^n (x+1)n 中的项 x m x^m xm 的系数模 p p p,根据二项式定理就是 ( n m ) m o d p \dbinom{n}{m}\bmod p (mn)modp。 ( 1 ) \quad(1) (1)
又根据引理 2 2 2 有
( x + 1 ) n ≡ ( x + 1 ) p ⌊ n p ⌋ ( x + 1 ) n m o d p ≡ ( x p + 1 p ) ⌊ n p ⌋ ( x + 1 ) n m o d p ≡ ( x p + 1 ) ⌊ n p ⌋ ( x + 1 ) n m o d p ( m o d p ) \begin{aligned} (x+1)^n &\equiv (x+1)^{p\left\lfloor\frac{n}{p}\right\rfloor}(x+1)^{n\bmod p}\\ &\equiv (x^p+1^p)^{\left\lfloor\frac{n}{p}\right\rfloor}(x+1)^{n\bmod p}\\ &\equiv (x^p+1)^{\left\lfloor\frac{n}{p}\right\rfloor}(x+1)^{n\bmod p}\pmod p \end{aligned} (x+1)n≡(x+1)p⌊pn⌋(x+1)nmodp≡(xp+1p)⌊pn⌋(x+1)nmodp≡(xp+1)⌊pn⌋(x+1)nmodp(modp)我们发现:
( x p + 1 ) ⌊ n p ⌋ (x^p+1)^{\left\lfloor\frac{n}{p}\right\rfloor} (xp+1)⌊pn⌋ 中的各项的次数均为 p p p 的倍数。
( x + 1 ) n m o d p (x+1)^{n\bmod p} (x+1)nmodp 中的各项的次数最多为 p − 1 p-1 p−1。
注意到第 2 2 2 条,次数最多只能到 p − 1 p-1 p−1,所以要想得到 x m x^m xm,只有一种方法:令 m = p q + r ( r < p ) m=pq+r(r<p) m=pq+r(r<p),则在 ( x p + 1 ) ⌊ n p ⌋ (x^p+1)^{\left\lfloor\frac{n}{p}\right\rfloor} (xp+1)⌊pn⌋ 中取到 q q q(即 ⌊ m p ⌋ \left\lfloor\frac{m}{p}\right\rfloor ⌊pm⌋)个 x p x^p xp,然后在 ( x + 1 ) n m o d p (x+1)^{n\bmod p} (x+1)nmodp 取到 r r r(即 m m o d p m\bmod p mmodp)个 x x x。
方法数为 ( ⌊ n p ⌋ q ) ⋅ ( n m o d p r ) ≡ ( ⌊ n p ⌋ ⌊ m p ⌋ ) ⋅ ( n m o d p m m o d p ) ( m o d p ) ( 2 ) \dbinom{\left\lfloor\frac{n}{p}\right\rfloor}{q}\cdot\dbinom{n\bmod p}{r}\equiv\dbinom{\left\lfloor\frac{n}{p}\right\rfloor}{\left\lfloor\frac{m}{p}\right\rfloor}\cdot\dbinom{n\bmod p}{m\bmod p}\pmod p\quad(2) (q⌊pn⌋)⋅(rnmodp)≡(⌊pm⌋⌊pn⌋)⋅(mmodpnmodp)(modp)(2)
由 ( 1 ) ( 2 ) (1)(2) (1)(2) 可得
( n m ) ≡ ( ⌊ n p ⌋ ⌊ m p ⌋ ) ⋅ ( n m o d p m m o d p ) ( m o d p ) \dbinom{n}{m}\equiv \dbinom{\left\lfloor\frac{n}{p}\right\rfloor}{\left\lfloor\frac{m}{p}\right\rfloor}\cdot \dbinom{n\bmod p}{m\bmod p}\pmod p (mn)≡(⌊pm⌋⌊pn⌋)⋅(mmodpnmodp)(modp)证毕。
预处理处 ∀ i ∈ [ 0 , p ) \forall i\in[0,p) ∀i∈[0,p), i ! i! i! 和 i n v ( i ! ) inv(i!) inv(i!)。然后对于单个询问, ( ⌊ n p ⌋ ⌊ m p ⌋ ) \dbinom{\left\lfloor\frac{n}{p}\right\rfloor}{\left\lfloor\frac{m}{p}\right\rfloor} (⌊pm⌋⌊pn⌋) 部分递归去跑,因为 p ≥ 2 p\ge 2 p≥2,所以是 log n \log n logn 的, ( n m o d p m m o d p ) \dbinom{n\bmod p}{m\bmod p} (mmodpnmodp) 部分就是预处理的。
时间复杂度为 O ( p + T log n ) \mathcal{O}(p+T\log n) O(p+Tlogn),其中 T T T 为询问次数。
可以发现瓶颈在于线性的预处理,所以 p p p 不能太大。
Code \text{Code} Code
//18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
typedef long long ll;
using namespace std;
const int MAXN = 1e5 + 5;
int p;
int qpow(int a, int b)
{
int base = a, ans = 1;
while (b)
{
if (b & 1)
{
ans = (ll)ans * base % p;
}
base = (ll)base * base % p;
b >>= 1;
}
return ans;
}
int inv(int a)
{
return qpow(a, p - 2);
}
int fac[MAXN], inv_fac[MAXN];
void init()
{
fac[0] = 1;
for (int i = 1; i < p; i++)
{
fac[i] = (ll)fac[i - 1] * i % p;
}
inv_fac[p - 1] = inv(fac[p - 1]);
for (int i = p - 2; i >= 0; i--)
{
inv_fac[i] = (ll)inv_fac[i + 1] * (i + 1) % p;
}
}
int C(int n, int m)
{
if (m > n)
{
return 0;
}
return (ll)fac[n] * inv_fac[n - m] % p * inv_fac[m] % p;
}
int Lucas(int n, int m)
{
if (!m)
{
return 1;
}
return (ll)Lucas(n / p, m / p) * C(n % p, m % p) % p;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
int n, m;
scanf("%d%d%d", &n, &m, &p);
init();
printf("%d\n", Lucas(n + m, n));
}
return 0;
}