[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;
}
posted @ 2024-06-18 22:06  是一只小蒟蒻呀  阅读(8)  评论(0编辑  收藏  举报