模运算 | 模运算除法 | 组合数
一、模运算
常识的:
模运算 对 加减乘 是没有影响的:
Tip
特别的, 对于 减法, 为了不会出现负数, 因此我们通常进行如下处理:
但是对于除法, 它就不得货啦:
比如 , 但是 , 这甚至都不是一个整数.
这里直接放计算方法:
其中, 是一个质数, 并且 , 并且 , 其中 为正整数.
Note
实际上这个就是 乘法逆元
:
在模条件下, 除以一个数 %b%, 等价于乘上另一个数 %xp$ 条件下, 除以 %b% 情况下的 乘法逆元.
constexpr int mod = 1e9 + 7;
template <typename T>
constexpr T qpow(T a, T b, T m) {
T res;
while (b) {
if (b & 1)
res = res * a % m;
b >>= 1;
a = a * a % m;
}
return res;
}
int a = 2233 * 64, b = 2233, res = 0;
res = ((a % mod) + (b % mod)) % mod;
res = ((a % mod) - (b % mod) + mod) % mod;
res = ((a % mod) * (b % mod)) % mod;
res = (a % mod) * (qpow(b, mod - 2, mod) % mod) % mod;
二、组合数学
2.1 排列数
2.2 组合数
预处理求解组合数, 预处理, 查询
const int MOD = 1'000'000'007;
const int MX = 100'001; // 根据题目数据范围修改
long long F[MX]; // F[i] = i!
long long INV_F[MX]; // INV_F[i] = i!^-1 = qpow(i!, MOD-2)
long long qpow(long long x, int n) {
long long res = 1;
for (; n; n /= 2) {
if (n % 2) {
res = res * x % MOD;
}
x = x * x % MOD;
}
return res;
}
// C(n, m) = n! / (m! * (n - m)!)
// = (n!) * (1 / m!) * (1 / (n - m)!)
auto init = [] {
F[0] = 1; // 0! = 1
// 递推求解 n!
// n! = n * (n - 1)!
for (int i = 1; i < MX; i++) {
F[i] = F[i - 1] * i % MOD;
}
// 反推 1 / n!
// 1 / (n - 1)! = (1 / n!) * n
INV_F[MX - 1] = qpow(F[MX - 1], MOD - 2); // 1 / n!
// a * b^{p - 2} % p
// 1 * (n!)^{p - 2} % p
for (int i = MX - 1; i; i--) {
INV_F[i - 1] = INV_F[i] * i % MOD;
}
return 0;
}();
// 从 n 个数中选 m 个数的方案数
long long comb(int n, int m) {
// C(n, m) = (n!) * (1 / m!) * (1 / (n - m)!)
return m < 0 || m > n
? 0
: F[n] * INV_F[m] % MOD * INV_F[n - m] % MOD;
}
class Solution {
public:
int solve(vector<int>& nums) {
// 预处理的逻辑写在 class 外面, 这样只会初始化一次
}
};
Tip
核心思想:
其中, 递推求解,
通过 即 求得.
然后 递归求解即可~