在模 下的乘法逆元不唯一,但是根据前面证明的消去律,它们都 mod 同余。换言之,在 mod 意义下,乘法逆元唯一。
例子:模 下的乘法逆元
0
1
2
3
4
5
6
7
8
乘法逆元
模 意义下的
即便 不是整数,若 互素,则存在无穷多个整数 使得 ,并且这样的 模 都同余,我们写
在模 的意义下,我们可把整数 等同于分数 。
求模 逆元的方法
设 为素数,,我们求 在模 下的逆元,记作 。
法一 根据费马小定理,有
取 即可。用快速幂算法计算 ,时间是 。
intinverse(int x, int p){
int ans = 1;
int n = p - 2;
while (n > 0) {
if (n & 1) ans = (longlong) ans * x % p;
x = (longlong) x * x % p;
n >>= 1;
}
return ans;
}
法二 的逆元是 。对 ,用带余除法把 表为 ,其中 。注意到
两边取逆元,得
两边乘以 给出
据此我们得到一个求 的递归算法:
intinverse(int x, int p){
if (x == 1) return1;
return p - (longlong) (p / x) * inverse(p % x) % p;
}
法二的时间复杂度
intinverse(int x, int p){
if (x == 1) return1;
return p - (longlong) (p / x) * inverse(p % x) % p;
}
intinverse_table(int p){
vector<int> inverse(p);
inverse[1] = 1;
for (int x = 2; x < p; x++)
inverse[x] = p - (longlong) (p / x) * inverse[p % x] % p;
}
用扩展欧几里得算法求逆元
一般来说,模数未必是素数。
回忆到对于整数 ,扩展欧几里得算法给出整数 使得 。若 ,此 恰是 在模 下的乘法逆元。
intinverse(int a, int n){ //计算a在模n下的逆元。要求a,n非负。int x, y;
int d = extgcd(a, n, x, y); // d是gcd(a,n)assert(d == 1); // 确保 a,n 互素return x;
}
求逆元的非递归写法
// 求a在模n下的乘法逆元intinverse(int a, int n){
int u = 0, v = 1;
while (a != 0) {
int q = n / a;
n -= q * a;
u -= q * v;
swap(n, a);
swap(u, v);
}
assert(n == 1);
return u; // u可能为负数
}
intinverse(int x, int p){
if (x == 1) return1;
return p - (longlong)(p / x) * inverse(p % x, p) % p;
}
intmain(){
ios::sync_with_stdio(0); cin.tie(0);
int n, p, k; cin >> n >> p >> k;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
vector<int> b(n); //后缀积
b[n - 1] = 1;
for (int i = n - 1; i >= 1; i--) b[i - 1] = (longlong)b[i] * a[i] % p;
longlong sum = 0, t = 1, prod = 1;
for (int i = 0; i < n; i++) {
prod = prod * k % p;
sum += t * prod % p * b[i] % p;
t = t * a[i] % p;
}
cout << sum % p * inverse((int)t, p) % p << '\n';
}
常数优化
考虑用递推法计算
对于 ,令
所求正是 。有递推式
这样就无需求序列 的后缀积了。
代码
intmain(){
ios::sync_with_stdio(0);
cin.tie(0);
int n, p, k;
cin >> n >> p >> k;
longlong sum = 0, t = 1, prod = 1;
for (int i = 0; i < n; i++) {
int a;
cin >> a;
prod = prod * k % p;
sum = (sum * a + t * prod) % p;
t = t * a % p;
}
cout << sum * inverse((int)t, p) % p << '\n';
}
int p;
unsignedlonglong q; // q = -1ull / p + 1; intmod(longlong x){
int r = x - ((__int128) x * q >> 64) * p;
if (r < 0) r += p;
return r;
}
int p;
unsignedlonglong q;
intmod(longlong x){
int r = x - ((__int128) x * q >> 64) * p;
if (r < 0) r += p;
return r;
}
intmain(){
ios::sync_with_stdio(0);
cin.tie(0);
int n, k; cin >> n >> p >> k;
q = -1ull / p + 1; // -1ull是2的64次方减1longlong sum = 0, t = 1, prod = 1;
for (int i = 0; i < n; i++) {
int a; cin >> a;
prod = mod(prod * k);
sum = mod(sum * a + t * prod);
t = t * a % p;
}
cout << sum * inverse((int)t, p) % p << '\n';
}