【模板】乘法逆元 2
题目描述
给定 n n n 个正整数 a i a_i ai ,求它们在模 p p p 意义下的乘法逆元。
由于输出太多不好,所以将会给定常数
k
k
k,你要输出的答案为:
∑
i
=
1
n
k
i
a
i
\sum\limits_{i=1}^n\frac{k^i}{a_i}
i=1∑naiki
答案对 p p p 取模。
输入格式
第一行三个正整数
n
,
p
,
k
n,p,k
n,p,k,意义如题目描述。
第二行
n
n
n 个正整数
a
i
a_i
ai,是你要求逆元的数。
输出格式
输出一行一个整数,表示答案。
样例 #1
样例输入 #1
6 233 42
1 4 2 8 5 7
样例输出 #1
91
提示
对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 1 0 5 1\le n \le 10^5 1≤n≤105。
对于 100 % 100\% 100% 数据, 1 ≤ n ≤ 5 × 1 0 6 1\le n \le 5\times 10^6 1≤n≤5×106, 2 ≤ k < p ≤ 1 0 9 2\le k < p \le 10^9 2≤k<p≤109, 1 ≤ a i < p 1\le a_i < p 1≤ai<p,保证 p p p 为质数。
AC题解—快速幂+逆元:
// 线性求任意 n 个数的逆元,这个题目就是他的一个变种
#include <iostream>
using namespace std;
typedef long long ll;
ll n, p, k;
ll qpow(ll a, ll b)
{
a %= p;
ll res = 1;
while (b > 0)
{
if (b & 1)
{
res = res * a % p;
}
a = a * a % p;
b >>= 1;
}
return res;
}
inline int read()
{
int f = 1, x = 0;
char ch;
do
{
ch = getchar();
if (ch == '-')
f = -1;
} while (ch < '0' || ch > '9');
do
{
x = x * 10 + ch - '0';
ch = getchar();
} while (ch >= '0' && ch <= '9');
return f * x;
}
int main()
{
n = read();
p = read();
k = read();
ll ans = 0;
ll temp = k;
ll a[n + 5], s[n + 5], sv[n + 5]; // inv[n + 5](可以不存); // a[i]给定的数 s[i]表示i个数的前缀积 sv[i]表示s[i]的逆元 inv[i]表示a[i]的逆元
for (int i = 1; i <= n; i++)
{
a[i] = read();
}
s[0] = 1;
for (int i = 1; i <= n; ++i)
s[i] = s[i - 1] * a[i] % p; // 求前缀积
sv[n] = qpow(s[n], p - 2); // 前缀积的逆元
for (int i = n; i >= 1; --i)
sv[i - 1] = sv[i] * a[i] % p;
// 因为 sv_n 是 n 个数的积的逆元,所以当我们把它乘上 a_n 时,就会和 a_n 的逆元抵消,于是就得到了 a_1 到 a_{n-1} 的积逆元,记为 sv_{n-1}。
// 同理我们可以依次计算出所有的 sv[i]
for (int i = 1; i <= n; ++i)
{
// inv[i] = sv[i] * s[i - 1] % p;
// a[i]^(-1)=sv[i]*s[i-1]--->前i个数的逆元*前i-1个数的积
// ans = (ans + (ll)inv[i] * temp) % p;
ans = (ans + (ll)(sv[i] * s[i - 1] % p) * temp) % p;
temp = (temp * k) % p;
}
printf("%lld", ans);
return 0;
}