逆元
逆元通常是用来解决除法求模问题的,求模运算有以下法则:
$$ \begin{cases} (a+b)\%c&=&(a\%c+b\%c)\%c\quad &加法法则\\[1ex] (a-b)\%c&=&(a\%c-b\%c)\%c\quad &减法法则\\[1ex] (a*b)\%c&=&(a\%c*b\%c)\%c\quad &乘法法则 \end{cases} $$可以发现,除法求模没有相应的法则。当计算 $\cfrac{a}{b}\%c$ 时,如果 $a$,$b$ 很大,不能在计算完之后取模,可以通过变换将除法变为乘法,然后就可以通过上面的公式取模了。
设 $b*k\%c=1$ ,则有
$$ \cfrac{a}{b}\%c=\cfrac{a}{b}\%c*(b*k\%c)=(a*k)\%c $$这样就把除法转化为了乘法,这里的 $k$ 就叫做 $b$ 关于 $c$ 的乘法逆元,写成数学表达式就是
$$ bk\equiv1\pmod c $$定义
若 $p$ 为素数,$\gcd (a,p)=1$ (即 $a,p$ 互质),有 $a^{p-1}\equiv1\pmod{p}$
另一种形式:对于任意整数 $a$,有 $a^p\equiv a\pmod{p}$
乘法逆元形式:$a\times a^{p-2}\equiv1\pmod p$ ,即 $a$ 的逆元是 $a^{p-2}$。
证明
数学归纳法证明:
显然 $1^p\equiv 1\pmod{p}$,假设 $a^p\equiv a\pmod{p}$ 成立,需要证明 $(a+1)^p\equiv a+1\pmod{p}$。
$$ \begin{aligned} &二项式定理: (a+1)^p=a^p+\begin{pmatrix}p\\1 \end{pmatrix}a^{p-1}+\begin{pmatrix}p\\2 \end{pmatrix}a^{p-2}+\cdots+\begin{pmatrix}p\\p-1 \end{pmatrix}a+1\\[1ex] &\because 1\le k\le p-1时,\begin{pmatrix}p\\k \end{pmatrix}=\cfrac{p(p-1)\cdots(p-k+1)}{k!}\\ &\therefore \begin{pmatrix}p\\1 \end{pmatrix}\equiv \begin{pmatrix}p\\2 \end{pmatrix}\equiv \cdots\equiv \begin{pmatrix}p\\p-1 \end{pmatrix}\equiv0\pmod{p}\\[1ex] &\therefore(a+1)^p\equiv a^p+1\pmod{p}\\[1ex] &\because a^p\equiv a\pmod{p}\\[1ex] &\therefore (a+1)^p\equiv a+1\pmod{p}\\[1ex] &\therefore原式得证 \end{aligned} $$求解组合数
求解 $C_m^n$ ,最终结果对 $10^9+7$ 取模。
方法一:由杨辉三角有 $C_m^n = C_{m-1}^{n-1} + C_{m-1}^{n}$ ,可以通过动态规划求解,时间复杂度 $O(N^2)$
int f(int m, int n) {
int[][] dp = new int[m + 1][n + 1];
for (int i = 0; i < m + 1; i++)
dp[i][0] = 1; // C_m^0 = 1
for (int i = 1; i < m + 1; i++) {
for (int j = 1; j < n + 1; j++) {
dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % MOD;
}
}
return dp[m][n];
}
方法二:快速幂 + 费马小定理
$$ \begin{align*} C_m^n &= \cfrac{m!}{(m-n)!*n!}\%MOD\\[1ex] &= \cfrac{(m-n+1)*(m-n+2)*...*m}{1*2*...*n}\% MOD \\ \end{align*} $$设 $a=(m-n+1)*(m-n+2)*\ldots *m,b=1*2*\ldots *n$,那么现在就要求 $b$ 对于 $p$ 的乘法逆元。将原式拆分为 $\cfrac{a}{1}*\cfrac{a}{2}*...\cfrac{a}{n}$,则只需要计算 $i(1\le i\le n)$ 的逆元。根据费马小定理得逆元 $k = i^{p-2} \%p$ 。
// 非递归快速幂对p取模
public int nqpow(int a, int n, int p) {
long res = 1, a1 = a;
while (n != 0) {
if ((n & 1) == 1) { // n是奇数
res = res * a1 % p; // 注意此处和下面不能缩写成 res *= a1 % p;
}
a1 = a1 * a1 % p;
n >>= 1;
}
return (int) res;
}
int f(int m, int n) {
long res = 1;
for (int i = m - n + 1; i <= m; i++)
res = res * i % MOD; // 计算分子
for (int i = 1; i <= n; i++)
res = res * nqpow(i, MOD - 2, MOD) % MOD; // 计算分母
return (int) res;
}
参考
[1] 欧拉定理 & 费马小定理