--- title: Ceizenpok's Formula tags: - Chinese Remainder Theorem - Combinatorics - GCD-LCM - Lucas's Theorem - Multiplicative Inverse - Prime id: "2209" categories: - - OI - Number Theory date: 2018-03-05 15:25:10 --- ## 题目描述 Dr. Ceizenp'ok from planet i1c5l became famous across the whole Universe thanks to his recent discovery - the Ceizenpok’s formula. This formula has only three arguments: $n, k$ and $m$, and its value is a number of $k$-combinations of a set of $n$ modulo $m$. While the whole Universe is trying to guess what the formula is useful for, we need to automate its calculation. ## 题意概述 求${n \choose k} \bmod m$。 数据范围:$1 \le n \le 10^{18}, \ 0 \le k \le n, \ 2 \le m \le 10^6$。 ## 算法分析 令 $$ x={n \choose k}, \ m=p_1^{a_1}p_2^{a_2} \cdots p_q^{a_q} $$ 可以列出方程组 $$ \left\\{ \begin{array}{c} x \equiv r_1 \pmod{p_1^{a_1}} \\\\ x \equiv r_2 \pmod{p_2^{a_2}} \\\\ \cdots \\\\ x \equiv r_q \pmod{p_q^{a_q}} \end{array} \right. $$ 由于模数两两互质,所以该方程组在模$m$意义下有唯一解。 考虑如何求$r_i$。实际上,我们要求的就是${n \choose k} \bmod p_i^{a_i}$。我们知道 $$ {n \choose k}={n! \over k!(n-k)!} $$ 那么只要求出$n! \bmod p_i^{a_i}, \ k! \bmod p_i^{a_i}, \ (n-k)! \bmod p_i^{a_i}$的值,就可以用逆元求出${n \choose k} \bmod p_i^{a_i}$。 对于如何求$n! \bmod p_i^{a_i}$,令 $$ f(n)=n! \bmod p_i^{a_i} $$ 由$x \equiv x+p_i^{a_i} \pmod{p_i^{a_i}}$,可得 $$ f(n)=\left(f\left(\left\lfloor{n \over p_i}\right\rfloor\right)\cdot p_i^{\lfloor n/p_i \rfloor}\cdot\left(\prod_{i \in [1, p_i^{a_i}], \ p_i\not\mid i}i\right)^{\lfloor n/p_i^{a_i} \rfloor}\cdot\prod_{i \in [1, n \bmod p_i^{a_i}], \ p_i\not\mid i}i\right)\bmod p_i^{a_i} $$ 但是$k!, \ (n-k)!$在模$p_i^{a_i}$意义下可能不存在逆元,因此需要将$n!, \ k!, \ (n-k)!$中的$p_i$因子提取出来,求出逆元后再乘回去。 这样就得到了所有$r_i$。用中国剩余定理求解方程组即可。 ## 代码 ```cpp /* * Your lucky number has been disconnected. */ #include #include #include #define int long long int power(int a, int b, int m) { int ret = 1; for (; b; b >>= 1) b & 1 && ((ret *= a) %= m), (a *= a) %= m; return ret; } void ex_gcd(int a, int b, int &x, int &y) { if (!b) { x = 1, y = 0; return; } ex_gcd(b, a % b, y, x), y -= a / b * x; } int get_inv(int a, int b) { int x, y; ex_gcd(a, b, x, y); return (x + b) % b; } int get_fac(int n, int p, int k) { int m = power(p, k, 1e9), ret = 1; for (; n; n /= p) { if (n / m) { int rec = 1; for (int i = 2; i < m; ++i) if (i % p) (rec *= i) %= m; (ret *= power(rec, n / m, m)) %= m; } for (int i = n % m; i > 1; --i) if (i % p) (ret *= i) %= m; } return ret; } int calc(int N, int K, int M, int p, int k) { int a = get_fac(N, p, k), b = get_fac(K, p, k), c = get_fac(N - K, p, k), cnt = 0; for (int i = N; i; i /= p) cnt += i / p; for (int i = K; i; i /= p) cnt -= i / p; for (int i = N - K; i; i /= p) cnt -= i / p; int m = power(p, k, 1e9), ret = a * get_inv(b, m) % m * get_inv(c, m) % m * power(p, cnt, m) % m; return ret * (M / m) % M * get_inv(M / m, m) % M; } signed main() { int N, K, M, ans = 0; scanf("%lld%lld%lld", &N, &K, &M); for (int i = 2, t = M; t > 1; ++i) if (!(t % i)) { int k = 0; for (; !(t % i); ++k, t /= i) ; (ans += calc(N, K, M, i, k)) %= M; } printf("%lld\n", ans); return 0; } ```