二项式定理与杨辉三角
杨辉三角
$1.$杨辉三角计算公式
组合公式 $ C_{n}^{r} $ = $ n $ !/ $ r $ ! $ (n - r) $ !。 把 $ C_{n}^{r} $ 称为二项式系数。 杨辉三角是二项式系数的典型应用:
仔细发现,可以得到:杨辉三角第 $ i $ 层上的数,是 $(1 + x)^i $ 展开后各项的系数。
推导得: $ (a + b)^n = \Sigma_{r = 0}^{n} C_{n}^{r} a^r b^{n - r} = \Sigma_{r = 0}^{n} C_{n}^{r} b^r a^{n - r} $,这个公式称为二项式定理。
当 $ n $ 较大,需要取模时,二项式系数有两种计算方法。
$(1).$利用递推公式:$ C_{n}^{r} = C_{n - 1}^{r} + C_{n - 1}^{r - 1} $。
$(2).$用逆元直接计算。
下面给出这两种思路的实现代码:
这里可以进行一些小优化,因为 $ 1/fact[i] = (i + 1)/fact[i + 1] $,所以,$ invfact[i] = (i + 1) \times invfact[i + 1] $。因此,求逆元的过程可以改写成如下形式:(先求出最后一项的逆元,然后逆推计算前面的项的逆元)。
P1313 [NOIP2011 提高组] 计算系数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
$ x^n \times y^m $ 项的系数是:$ C_{k}^{n} a^n b^{k - n} $。直接计算该式子即可。
#include <bits/stdc++.h> #define L(i, j, k) for (int i = (j); i <= (k); ++i) #define R(i, j, k) for (int i = (j); i >= (k); --i) #define i64 long long #define LL unsigned long long #define pii std::pair<int, int> inline int read() { bool sym = false; int res = 0; char ch = getchar(); while (!isdigit(ch)) sym |= (ch == '-'), ch = getchar(); while (isdigit(ch)) res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar(); return sym ? -res : res; } // 思路一: 利用递推式处理 const int N = 2200, mod = 10007; // 模数 i64 C[N][N]; /* 用递推法计算时, 需要注意两个地方: 1. 边界为 C[0][0] = 1; 2. C[i][0] = 1; (防止当 j = 0时, j - 1 越界) */ void init() { C[0][0] = 1; for (int i = 1; i <= N - 1; i++) { C[i][0] = 1; for (int j = 1; j <= i; j++) { /* 算法竞赛中认为, 当 r > n 时, C[n][r] = 0 */ C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod; } } } i64 qpow(int x, int y) { x %= mod; i64 res = 1; while (y) { if (y & 1) res = res * x % mod; y >>= 1; x = x * x % mod; } return res; } int main() { init(); int a = read(), b = read(), k = read(), n = read(), m = read(); printf("%lld\n", C[k][n] * qpow(a, n) * qpow(b, m) % mod); return 0; }
P2822 [NOIP2016 提高组] 组合数问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
因为 $ k $ 是固定的, 所以不难想到的一个思路是, 通过观察 $ C_{i}^{j} $ 模 $ k $ 是否为 $ 0 $ 来判断 $ C_{i}^{j} $ 是否是 $ k $ 的倍数 (即计算组合数时,对 $ k $ 取模), 为了避免重复进行不必要的计算, 我们采用二维前缀和优化, 总的时间复杂度为 $ O(n^2) $。
#include <bits/stdc++.h> #define i64 long long inline int read() { bool sym = false; int res = 0; char ch = getchar(); while (!isdigit(ch)) sym |= (ch == '-'), ch = getchar(); while (isdigit(ch)) res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar(); return sym ? -res : res; } const int N = 2e3 + 100; i64 C[N][N]; i64 sum[N][N]; int k; int main() { int t = read(), k = read(); C[0][0] = 1; for (int i = 1; i <= 2000; i++) { C[i][0] = 1; for (int j = 1; j <= i; j++) { C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % k; } } for (int i = 0; i <= 2000; i++) { for (int j = 0; j <= i; j++) { sum[i][j] = (C[i][j] == 0); } } for (int i = 1; i <= 2000; i++) { for (int j = 1; j <= 2000; j++) { sum[i][j] = (sum[i - 1][j] + sum[i][j] + sum[i][j - 1] - sum[i - 1][j - 1]); } } while (t--) { int n = read(), m = read(); printf("%lld\n", sum[n][m]); } return 0; }