题目
给定 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 取模。
输入格式
第一行三个正整数 n,p,k 意义如题目描述。
第二行 n 个正整数 a_i ,是你要求逆元的数。
输出格式
输出一行一个整数,表示答案。
输入输出样例
输入
6 233 42
1 4 2 8 5 7
输出
91
说明/提示
对于 30 分的数据:
1
≤
n
≤
1
0
5
1≤n≤10^5
1≤n≤105
对于全部数据:
1
≤
n
≤
5
×
1
0
6
1≤n≤5×10^6
1≤n≤5×106
2
≤
k
<
p
≤
1
0
9
2≤k<p≤10^9
2≤k<p≤109
1
≤
a
i
<
p
1\le a_i < p
1≤ai<p
保证p为质数。
请注意常数优化 \color{red}\text{请注意常数优化} 请注意常数优化
思路
主流的做法是什么前缀积、后缀积,还有一堆常数优化。
但在一篇题解上看到更简单的方法:
首先边读入边通分,
我们由费马小定理:
a
p
−
1
≡
1
(
m
o
d
p
)
a^{p-1}≡1 (mod\ p)
ap−1≡1(mod p),结合乘法逆元:
a
x
≡
1
(
m
o
d
p
)
ax≡1 (mod\ p)
ax≡1(mod p),
可得:
x
=
a
p
−
2
x=a^{p-2}
x=ap−2,再用快速幂求出即可
代码
#include<iostream>
#include<cstdlib>
#define ll unsigned long long
using namespace std;
ll n,p,k,kt,a,at,b=1;
ll read()
{
char ch=getchar();
ll x=0;
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')
{
x=(x<<3)+(x<<1)+ch-48;
ch=getchar();
}
return x;
}
ll fpow(ll a,ll b) //快速幂
{
ll ans=1;
while(b)
{
if(b&1) ans=(ans*a)%p;
a=a*a%p;
b>>=1;
}
return ans;
}
int main()
{
n=read();
p=read();
k=read();
kt=k%p;
for(ll i=1;i<=n;i++)
{
at=read();
a=(b*kt%p+a*at%p)%p;
b=b*at%p;
kt=kt*k%p;
}
printf("%lld",a*fpow(b,p-2)%p);
}
该博客介绍了一种利用费马小定理和快速幂计算模逆元的简单方法,适用于处理大量数据。通过先边读入边通分,然后应用费马小定理的性质x=ap−2,可以高效地计算出每个数的模逆元,并最终求出给定数列在模p意义下的乘积逆元。代码中展示了如何实现这一过程。
643

被折叠的 条评论
为什么被折叠?



