题解 【洛谷 P5431 【模板】乘法逆元2】
\(\large\mathcal{Decription}\)
给定 \(n,p,k\) 及数列 \(a_i\). 求:
\[(\sum\limits_{i=1}^n \dfrac{k^i}{a_i}) \bmod p
\]
\(1\le n\le 5\times 10 ^ 6, 2\le k < p \le 10^9, 1\le a_i < p.\)
\(\large\mathcal{Solution}\)
题目即要求 \(a_1, a_2, \cdots a_n\) 对 \(p\) 的逆元。算是一个 \(\text{trick}\) 吧,下面给出 \(\mathcal{O}(n)\) 离线求逆元的算法。
记 \(\Pi(i)\) 表示 \(a_1a_2...a_i\bmod p\), 容易将其 \(\mathcal{O}(n)\) 处理出来。
记 \(Inv(i)\) 表示 \(\Pi(i)\) 对 \(p\) 的逆元,我们容易得到公式:
\[Inv(i) = Inv(i + 1)\times a_{i+1}\bmod p
\]
那么就能 \(\mathcal{O}(n)\) 处理出 \(Inv(i).\)
记 \(inv(i)\) 表示 \(a_i\) 对 \(p\) 的逆元,那么有:
\[inv(i) = Inv(i)\times \Pi(i-1)\bmod p
\]
那么就能 \(\mathcal{O}(n)\) 处理出 \(inv(i).\) 至此,问题得解。
\(\large\mathcal{Code}\)
#include <bits/stdc++.h>
#define reg register
using namespace std;
const int N = 5000010;
int n, p, k, a[N];
int pi[N], inv_pi[N], inv[N]; // inv_pi 即 Inv 数组
inline int read()
{
reg int x = 0;
reg char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while ('0' <= ch && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x;
}
inline int qpow(reg int a, reg int b, reg int p)
{
reg int res = 1;
for (; b; b >>= 1)
{
if (b % 2 == 1) res = 1ll * res * a % p;
a = 1ll * a * a % p;
}
return res;
}
int main()
{
n = read(); p = read(); k = read();
for (reg int i = 1; i <= n; ++ i) a[i] = read();
pi[1] = a[1];
for (reg int i = 2; i <= n; ++ i) pi[i] = 1ll * pi[i - 1] * a[i] % p;
inv_pi[n] = qpow(pi[n], p - 2, p);
for (reg int i = n - 1; i >= 1; -- i) inv_pi[i] = 1ll * inv_pi[i + 1] * a[i + 1] % p;
inv[1] = inv_pi[1];
for (reg int i = 2; i <= n; ++ i) inv[i] = 1ll * pi[i - 1] * inv_pi[i] % p;
reg int ans = 0;
for (reg int i = n; i >= 1; -- i) ans = 1ll * (ans + inv[i]) * k % p;
printf("%d\n", ans);
return 0;
}