【模板】模意义下的乘法逆元 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 为质数。
提示:本题时间限制较为严格,请注意使用较快的 IO 方式。
思路
根据题目,我们要求的是:
∑
i
=
1
n
k
i
a
i
=
∑
i
=
1
n
k
i
⋅
w
i
−
1
\sum_{i=1}^{n}{\frac{k^i}{a_i}}=\sum_{i=1}^{n}{k^i\cdot w_i^{-1}}
i=1∑naiki=i=1∑nki⋅wi−1
我们考虑先求出
w
w
w 序列的前缀积,记为
s
s
s :
s
i
=
∏
j
=
1
i
w
j
s_i=\prod_{j=1}^{i}{w_j}
si=j=1∏iwj
记
w
w
w 序列前缀积的逆元为
t
t
t ,此时如果我们知道这个序列,我们就可以知道
w
w
w 中任意一个数的逆元:
w
i
−
1
=
s
i
−
1
×
t
i
w_i^{-1}=s_{i-1}\times t_i
wi−1=si−1×ti
我们还知道:前缀积的逆元就等于逆元的前缀积 ,所以我们只要求出
t
n
t_n
tn 就可以线性递推出整个
t
t
t 序列了:
t
n
=
s
n
p
−
2
t_n=s_n^{p-2}
tn=snp−2
t
i
=
w
i
+
1
×
t
i
+
1
t_i=w_{i+1}\times t_{i+1}
ti=wi+1×ti+1
这样我们就求得了
w
w
w 序列的逆元,然后再累加一下再乘
k
k
k 就是我们的答案了。
代码
#include<cstdio>
#define int long long
using namespace std;
const int N = 5e6+10;
int w[N];
int s[N],ins[N];//ins逆元前缀和
int n,p,k;
int ans;
int read(){
int x=0;
char ch=getchar();
while(!(ch>='0'&&ch<='9')){
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x;
}
int qmi(int a,int b,int p){
int res=1;
while(b){
if(b&1)res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
signed main(){
n=read(),p=read(),k=read();
s[0]=1;
for(int i=1;i<=n;i++){
w[i]=read();
s[i]=s[i-1]*w[i]%p;
}
ins[n]=qmi(s[n],p-2,p);
for(int i=n-1;i;i--){
ins[i]=ins[i+1]*w[i+1]%p;
}
for(int i=n;i;i--){
ans=(ans+ins[i]*s[i-1]%p)*k%p;
}
printf("%lld",ans);
return 0;
}