1. 程式人生 > >[Codeforces 100633J]Ceizenpok’s formula

[Codeforces 100633J]Ceizenpok’s formula

理論 bit code 第一個 int ret def 方程組 for

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/pi, 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; }

[Codeforces 100633J]Ceizenpok’s formula