[Codeforces 100633J]Ceizenpok’s formula

Description

题库链接

求 $$C_n^m \mod p$$

$1\leq m\leq n\leq 10^{18},2\leq p\leq 1000000$

Solution

一般的 $Lucas$ 是在模数 $p$ 是质数的条件下适用的。我们来考虑 $p$ 不是质数的条件。

我们对 $p$ 进行唯一分解,记 $p=p_1^{k_1}p_2^{k_2}\cdots p_q^{k_q}$ ,由于形同 $p_i^{k_i}$ 的部分是互质的,显然我们可以用 $CRT$ 合并。

列出方程组: $$\left{ \begin{array}{c} ans\equiv c_1\pmod {{p_1}^{k_1}}\ ans\equiv c_2\pmod {{p_2}^{k_2}}\ ...\ ans\equiv c_q\pmod {{p_q}^{k_q}}\ \end{array} \right.$$ ,对于每个 $c_i$ ,表示 $C_n^m$ 在 $\mod p_i^{k_i}$ 下的结果。由解的唯一性,我们可以证明这个 $ans$ 就是我们要求的。
根据 $C_n^m=\frac{n!}{m!(n-m)!}$ 我们只要求出 $n!\mod p_i^{k_i},m!\mod p_i^{k_i},(n-m)!\mod p_i^{k_i}$ ,再用逆元的那套理论就可以求 $c_i$ 了。

考虑如何求 $n!\mod p_i^{k_i}$ 。容易发现 $n!=\left(\prod\limits_{j=1}^n j^{[p_i\nmid j]}\right)\cdot\left(p_i^{\left\lfloor\frac{n}{p_i}\right\rfloor}\right)\cdot\left(\left\lfloor\frac{n}{p_i}\right\rfloor\large! \right)$ 上述式子分为三个部分,第一个部分显然在模 $p_i^{k_i}$ 下,是以 $p_i^{k_i}$ 为周期的。可以周期内找循环节算,周期外的暴力算;第二部分可以直接算;第三部分可以递归求解。

另外注意的是求组合逆元的时候,存在阶乘中的某一个数可能还有 $p_i$ 这个质因子,不能直接算。直接把 $p_i$ 全部提出来,最后求完逆元后再补回去。求 $n!$ 内质因子 $p$ 的个数可以用 $\sum\limits_{i=1}^{+\infty} \left\lfloor\frac{n}{p^i}\right\rfloor$ 来求。

Code

//It is made by Awson on 2018.2.10
#include <bits/stdc++.h>
#define LL long long
#define dob complex<double>
#define Abs(a) ((a) < 0 ? (-(a)) : (a))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
#define writeln(x) (write(x), putchar('\n'))
#define lowbit(x) ((x)&(-(x)))
using namespace std;
void read(LL &x) {
    char ch; bool flag = 0;
    for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar());
    for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar());
    x *= 1-2*flag;
}
void print(LL x) {if (x > 9) print(x/10); putchar(x%10+48); }
void write(LL x) {if (x < 0) putchar('-'); print(Abs(x)); }

LL n, m, p;

LL quick_pow(LL a, LL b, LL p) {
    LL ans = 1;
    while (b) {
    if (b&1) ans = ans*a%p;
    b >>= 1, a = a*a%p;
    }
    return ans;
}
void ex_gcd(LL a, LL b, LL &x, LL &y) {
    if (b == 0) {x = 1, y = 0; return; }
    ex_gcd(b, a%b, x, y);
    LL t = x; x = y, y = t-a/b*y;
}
LL inv(LL a, LL p) {
    LL x, y; ex_gcd(a, p, x, y);
    return (x%p+p)%p;
}
LL mul(LL n, LL pi, LL pk) {
    if (!n) return 1;
    LL ans = 1;
    for (int i = 2; i <= pk; i++) if (i%pi != 0) ans = ans*i%pk;
    ans = quick_pow(ans, n/pk, pk);
    for (int i = 2; i <= n%pk; i++) if (i%pi != 0) ans = ans*i%pk;
    return ans*mul(n/pi, pi, pk)%pk;
}
LL C(LL n, LL m, LL pi, LL pk, LL p) {
    LL a = mul(n, pi, pk), b = mul(m, pi, pk), c = mul(n-m, pi, pk);
    LL k = 0;
    for (LL i = n; i; i /= pi) k += i/pi;
    for (LL i = m; i; i /= pi) k -= i/pi;
    for (LL i = n-m; i; i /= pi) k -= i/pi;
    return a*inv(b, pk)%pk*inv(c, pk)%pk*quick_pow(pi, k, pk)%pk;
}
LL ex_lucas(LL n, LL m, LL p) {
    LL ans = 0;
    for (LL i = 2, x = p; i <= x; i++)
    if (x%i == 0) {
        LL k = 1; while (x%i == 0) k *= i, x /= i;
        (ans += C(n, m, i, k, p)*(p/k)%p*inv(p/k, k)%p) %= p;
    }
    return ans;
}
void work() {
    read(n), read(m), read(p);
    writeln(ex_lucas(n, m, p));
}
int main() {
    work();
    return 0;
}
posted @ 2018-02-10 15:56  NaVi_Awson  阅读(228)  评论(0编辑  收藏  举报