如果 ax≡1(modb) a x ≡ 1 ( mod b ) 且 (a,b)=1 ( a , b ) = 1 ,则称 x x 是模 b b 意义下的 a a 的逆元,记为 a−1 a − 1 。
逆元可以用来在计算 tamodb t a mod b 时,转化为 ta−1modb t a − 1 mod b 。
根据逆元的定义,可以转化为 ax+by=1 a x + b y = 1 ,用拓展欧几里得算法求解。
\\求解单个数在模意义下的逆元(exgcd)
void Exgcd(int a, int b, int& x, int& y) {
if (b == 0) {
x = 1; y = 0;
return;
}
Exgcd(b, a % b, y, x);
y -= a / b * x;
}
int Mod_Inverse(int a, int p) {
int x, y;
Exgcd(a, p, x, y);
return (x + p) % p;
}
int Exgcd(int a, int b, int& x, int& y) {
if (a == 0 && b == 0) return -1;
if (b == 0) {
x = 1; y = 0;
return a;
}
int value = Exgcd(b, a % b, y, x);
y -= a / b * x;
return value;
}
int Mod_Inverse(int a, int p) {
int x, y, value = Exgcd(a, p, x, y);
return value == 1 ? (x + p) % p : -1;
}
特殊地,当 b b 是个素数时,由费马小定理,可得 ab−1≡1(modb) a b − 1 ≡ 1 ( mod b ) ,则 a−1≡ab−2(modb) a − 1 ≡ a b − 2 ( mod b ) ,使用快速幂计算即可。
接下来讲线性算法求解给定 n n , p p 求 1 1 ~ n n 中所有整数在模 p p 意义下的乘法逆元。
我们令 f(i)=i! f ( i ) = i ! , g(i)=(i!)−1 g ( i ) = ( i ! ) − 1 。
很明显, f(i)=(i−1)!∗i=f(i−1)∗i f ( i ) = ( i − 1 ) ! ∗ i = f ( i − 1 ) ∗ i , g(i)=((i+1)!)−1∗(i+1)=g(i+1)∗(i+1) g ( i ) = ( ( i + 1 ) ! ) − 1 ∗ ( i + 1 ) = g ( i + 1 ) ∗ ( i + 1 ) 。
这样,我们顺推出 f f 数组,再逆推出 g g 数组,那么 i−1=i!∗((i−1)!)−1=f(i)∗g(i−1) i − 1 = i ! ∗ ( ( i − 1 ) ! ) − 1 = f ( i ) ∗ g ( i − 1 ) 。
所有等式均在模 p p 意义下进行。
#include
#include
#include
#include
#define MAXN 3000010
long long f[MAXN], g[MAXN];
void Exgcd(int a, int b, int& x, int& y) {
if (b == 0) {
x = 1; y = 0;
return;
}
Exgcd(b, a % b, y, x);
y -= a / b * x;
}
int Mod_Reverse(int a, int p) {
int x, y;
Exgcd(a, p, x, y);
return (x + p) % p;
}
int main() {
int n, p;
scanf("%d%d", &n, &p);
f[0] = 1;
for (int i = 1; i <= n; i++)
f[i] = (f[i - 1] * i + p) % p;
g[n] = Mod_Reverse(f[n], p); //p是质数的话可以直接快速幂
for (int i = n - 1; i >= 1; i--)
g[i] = (g[i + 1] * (i + 1) + p) % p;
for (int i = 1; i <= n; i++)
printf("%lld\n", (f[i - 1] * g[i] + p) % p);
return 0;
}
首先
我们设 p=k∗i+r p = k ∗ i + r ,且 1<r<i<p 1 < r < i < p ,那么这个式子在模 p p 意义下就会得到 k∗i+r≡0(modm) k ∗ i + r ≡ 0 ( mod m ) ,两边同乘以 i−1,r−1 i − 1 , r − 1 可得:
由此我们就可以从前面推出当前的逆元了。
#include
#include
#include
#include
#define MAXN 3000005
long long inv[MAXN];
int main() {
int n, p;
scanf("%d%d", &n, &p);
inv[1] = 1;
puts("1");
for (int i = 2; i <= n; i++) {
inv[i] = (p - p / i) * inv[p % i] % p;
printf("%lld\n", inv[i]);
}
return 0;
}