乘法逆元进阶运用
线性求逆元
令 \(k= \left\lfloor\dfrac{p}{i}\right\rfloor,r=p\mod i\)。
有 \(p\equiv ik+r\equiv 0 \pmod p\)
在两边分别乘上 \(i^{-1}\)、\(j^{-1}\)。
得到
\((ik+r)(i^{-1}r^{-1})\equiv 0\pmod p\\ (k+ri^{-1})r^{-1}\equiv 0\pmod p\\ kr^{-1}+i^{-1}\equiv 0\pmod p\\ i^{-1}\equiv -kr^{-1}\pmod p\\ i^{-1}\equiv -\left\lfloor\dfrac{p}{i}\right\rfloor(p\mod i)^{-1}\pmod p\)
由于 \(0\le p\mod i <i\) 故递推式成立。
乘法逆元的积性——离线逆元
我们说对于乘法逆元有:\(a^{-1}\times b^{-1}\equiv (ab)^{-1}\pmod{p}\)。
证明: \(a\times a^{-1}\equiv 1\pmod p\),\(b\times b^{-1}\equiv 1\pmod p\)。
将两式相乘,\(a\times b\times a^{-1}\times b^{-1}\equiv 1\pmod p\),显然 \(a^{-1}\times b^{-1}\) 是 \(ab\) 的逆元。
于是乘法逆元积性得证,我们可以用它解决一些问题。
例如
前缀积。设 \(prod(i)\) 表示从 \(1\) 到 \(i\) 的前缀积,那么要求 \([j,i]\) 的区间积,需要 \(prod(i)/prod(j-1)\)。可是有时候乘积太大需要取模,那么这时候的除法需要乘法逆元,但是 \(prod\) 可能很大,逆元数组存不下(这里是线性逆元),这时候就要用到这条结论:前缀积的逆元,就是逆元的前缀积。
设 \(pre_i=\prod^i_{j=1}a_j\),\(pre_i^{-1}=\prod^n_{j=1}a_j^{-1}\)。
我们可以得到递推式:\(pre_i^{-1}=a_{i+1}^{-1}\times pre_{i+1}^{-1}(1\le i <n)\)。
也可以求单个值 \(a_j^{-1}=pre_{j-1}\times pre_j^{-1}(1\le j \le n)\)。
那么要线性求逆元,需要先求出 \(pre_n^{-1}\),可以用费马小定理(\(p\) 是质数)或Exgcd。
总时间复杂度 \(O(n+\log p)\),接近线性。
模板题:P5431【模板】乘法逆元 2
此题卡的很死,注意速度和运算空间
#include <bits/stdc++.h>
#define rei register int
#define ll long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define rep(i, s, n, c) for (register int i = s; i <= n; i+=c)
#define repd(i, s, n, c) for (register int i = s; i >= n; i-=c)
#define CHECK cout<<"WALKED"<<endl;
inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
#define pb push_back
#define ls id<<1
#define rs id<<1|1
const int INF = INT_MAX;
long long binpow(long long a, long long b, ll mod){long long res = 1; while (b > 0){if (b & 1) res = res * a % mod;a = a * a % mod; b >>= 1; } return res;}
using namespace std;
int a[5000006], pre[5000006], inv_pre[5000006], inv[5000006];
int main()
{
int n; ll p, k;
scanf("%d%lld%lld", &n, &p, &k);
pre[0] = 1;
for (register int i = 1; i <= n; i++) {
a[i] = read();
pre[i] = 1ll * pre[i - 1] * a[i] % p;
}
inv_pre[n] = binpow(pre[n], p - 2, p);
for (int i = n; i > 0; i--) {
inv[i] = 1ll * pre[i - 1] * inv_pre[i] % p;
inv_pre[i - 1] = 1ll * inv_pre[i] * a[i] % p;
}
ll ans = 0;
for (int i = 1, t = k; i <= n; i++) {
ans = (1ll * inv[i] * t % p + ans) % p;
t = 1ll * t * k % p;
}
printf("%lld\n", ans);
return 0;
}