P5431 【模板】模意义下的乘法逆元 2
题目描述
给定 \(n\) 个正整数 \(a_i\) ,求它们在模 \(p\) 意义下的乘法逆元。
由于输出太多不好,所以将会给定常数 \(k\),你要输出的答案为:
\[\sum\limits_{i=1}^n\frac{k^i}{a_i}
\]
答案对 \(p\) 取模。
- 线性递推公式\(i^{-1}=(p-\lfloor{p\over i})\rfloor\times (p\% i)^{-1}\pmod{p}\),可以以\(O(n)\)的时间复杂度求解\(1\sim n\)的连续正整数的逆元。详细参考:P3811 【模板】模意义下的乘法逆元 - chen_xing - 博客园
- 针对于这道题目而言,数据范围是\(2≤k<p≤10^9,1≤ai<p\),如果强制保证我们求解连续正整数的逆元的话,我们的时间复杂度是\(O(max\{a_1,a_2,\cdots,a_n\})<O(p)\),大概率是会超出时间限制的。
- 我们需要再找到一个可以不要求连续的\(O(n)\)时间复杂度的算法。
前缀积求解序列逆元
- 假设我们有一个序列\(a=[a_1,a_2,\cdots,a_n]\),我们需要在\(O(n)\)甚至更低的时间复杂度下求解\([a_1^{-1}\pmod{p},a_2^{-1}\pmod{p},\cdots,a_n^{-1}\pmod{p}]\)。
- 我们可以先计算\(a_{prod}\equiv\prod_{i=1}^na_i\pmod{p}\),该过程时间复杂度为\(O(n)\);
- 计算\(a_{prod}^{-1}\equiv(a_1\times a_2\times \cdots\times a_n)^{-1}\pmod{p}\)。这一步我们可以利用费马小定理或者扩展欧几里得定理求解,时间复杂度为\(O(log\ p)\);
- 如果我们维护一个前缀积数组
s,其中\(s_i=\prod_{j=1}^i a_j\pmod{p}\),那么当我们以下面的流程从后向前进行遍历时,我们就可以准确求出\(a_i^{-1}\pmod{p}\):\[\begin{flalign}& for\ i=n\ down\ to\ 1:\\&\quad\quad a_i^{-1}\equiv a_{prod}^{-1}\times s_{i-1}\equiv {a_1\times a_2\times\cdots \times a_{i-1}\over a_1\times a_2\times\cdots\times a_{i}}\equiv a_i^{-1}\pmod{p}\\& \quad\quad a_{prod}\equiv a_{prod}\times a_i\pmod{p}\end{flalign} \]该过程时间复杂度为\(O(n)\),空间复杂度为\(O(n)\)。 - 注意我们要求解的最终结果是\(\sum_{i=1}^n{k^i\over a_i}\pmod{p}\),我们把求和公式展开,提取公因式\(k\):\[{k\over a_1}+{k^2\over a_2}+\cdots+{k^n\over a_n}\equiv k({1\over a_1}+k({1\over a_2}+\cdots+k({1\over a_n})\cdots))\pmod{p} \]这意味着我们在求解\(a_i^{-1}\)的时候就可以直接进行对结果贡献的累加,无需再开辟空间存储\(a_i^{-1}\pmod{p}\)了。
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 5000005;
int a[MAXN];
int s[MAXN];
// fread 快速模板
char buf[1<<21], *p1 = buf, *p2 = buf;
inline char gc() {
if (p1 == p2) {
p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin);
if (p1 == p2) return EOF;
}
return *p1++;
}
inline int read() {
int x = 0;
char c = gc();
while (c < '0' || c > '9') c = gc();
while (c >= '0' && c <= '9') {
x = (x * 10) + (c - '0');
c = gc();
}
return x;
}
int qpow(int base, int exp, int mod) {
int res = 1;
base %= mod;
while (exp > 0) {
if (exp % 2 == 1) res = 1LL * res * base % mod;
base = 1LL * base * base % mod;
exp >>= 1;
}
return res;
}
int main() {
int n = read();
int p = read();
int k = read();
s[0] = 1;
for (int i = 1; i <= n; i++) {
a[i] = read();
s[i] = 1LL * s[i - 1] * a[i] % p;
}
// a ** (p - 1) equiv 1 (mod p) → a ** (p - 2) * a equiv 1 (mod p)
int aProdInv = qpow(s[n], p - 2, p);
int ans = 0;
for (int i = n; i > 0; i--) {
int aInv = 1LL * aProdInv * s[i - 1] % p;
ans = (1LL * ans * k + aInv) % p;
aProdInv = 1LL * aProdInv * a[i] % p;
}
ans = 1LL * ans * k % p;
printf("%d\n", ans);
return 0;
}
- 时间复杂度:\(O(n + log\ p)\rightarrow O(n)\)
- 空间复杂度:\(O(n)\)

P5431 【模板】模意义下的乘法逆元 2
浙公网安备 33010602011771号