[lnsyoj668/luoguP3807]Lucas定理
题意
原题链接
求$$(^n_{n+m}) \bmod p (p\le10^5\text{ 且为质数})$$
sol
Lucas定理:
\[(^n_m) \equiv (^{n/p}_{m/p}) \cdot (^{n\bmod p}_{m\bmod p}) (\bmod p)
\]
证明(源自OI-wiki)
考虑$\displaystyle\binom{p}{n} \bmod p $ 的取值,注意到\(\displaystyle\binom{p}{n} = \frac{p!}{n!(p-n)!}\),分子的质因子分解中 \(p\) 的次数恰好为 \(1\),因此只有当 \(n = 0\) 或 \(n = p\) 的时候 \(n!(p-n)!\) 的质因子分解中含有 \(p\),因此
\(\displaystyle\binom{p}{n} \bmod p = [n = 0 \vee n = p]\)。进而我们可以得出
\[\begin{align}
(a+b)^p &= \sum_{n=0}^p \binom pn a^n b^{p-n}\\
&\equiv \sum_{n=0}^p [n=0\vee n=p] a^n b^{p-n}\\
&\equiv a^p + b^p \pmod p
\end{align}
\]
注意过程中没有用到费马小定理,因此这一推导不仅适用于整数,亦适用于多项式。因此我们可以考虑二项式 \(f^p(x)=(ax^n + bx^m)^p \bmod p\) 的结果
\[\begin{align}
(ax^n + bx^m)^p &\equiv a^p x^{pn} + b^p x^{pm} \\
&\equiv ax^{pn} + bx^{pm}\\
&\equiv f(x^p)
\end{align}
\]
考虑二项式 \((1+x)^n \bmod p\),那么 \(\displaystyle\binom n m\) 就是求其在 \(x^m\) 次项的取值。使用上述引理,我们可以得到
\[\begin{align}
(1+x)^n &\equiv (1+x)^{p\lfloor n/p \rfloor} (1+x)^{n\bmod p}\\
&\equiv (1+x^p)^{\lfloor n/p \rfloor} (1+x)^{n\bmod p}
\end{align}
\]
注意前者只有在 \(p\) 的倍数位置才有取值,而后者最高次项为 \(n\bmod p \le p-1\),因此这两部分的卷积在任何一个位置只有最多一种方式贡献取值,即在前者部分取 \(p\) 的倍数次项,后者部分取剩余项,即
\[\displaystyle\binom{n}{m}\bmod p = \binom{\left\lfloor n/p \right\rfloor}{\left\lfloor m/p\right\rfloor}\cdot\binom{n\bmod p}{m\bmod p}\bmod p \]
看不懂没关系,蒟蒻也看不懂
那么解决这道题就很简单了,只需要使用递归依次枚举,\(n > m\)的情况返回\(0\),\(n\le p\)且\(m\le p\)的情况直接计算(通过预处理来解决)就可以了
代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 100005;
long long inv[N], f[N], fi[N];
void init(long long p){
f[0] = fi[0] = 1;
inv[1] = f[1] = fi[1] = 1;
for (long long i = 2; i <= p; i ++ ){
inv[i] = (p - p / i) * inv[p % i] % p;
f[i] = f[i - 1] * i % p;
fi[i] = fi[i - 1] * inv[i] % p;
}
}
long long lucas(long long m, long long n, long long p){
if (m < n) return 0;
if (m <= p && n <= p) return f[m] * fi[n] % p * fi[m - n] % p;
return lucas(m / p, n / p, p) * lucas(m % p, n % p, p) % p;
}
int main(){
long long n, m, p;
int T;
cin >> T;
while (T -- ){
cin >> n >> m >> p;
init(p);
cout << lucas(n + m, n, p) << endl;
}
return 0;
}