洛谷题目链接
刚开始做乘法逆元还是有点懵逼的~
以下式子都在模(p)意义下进行
我们把式子改一下,变成:$$sumlimits_{i=1}nki imes a_i^{-1}$$
我们先算出(a_i)的前缀积:$$s[i]=s[i-1] imes a_i$$
我们发现只要算出每一个前缀积的逆元(t_i),每一个(a_i)的逆元都好求了:$$a_i^{-1}=t_i imes s_{i-1}$$
那么怎么求每一个前缀积的逆元呢,我们可以先把$t_n运用费马小定理求出来: $$t_n=s_n^{p-2}$$
再根据:$$t_i=t_{i+1}*a_{i+1}$$
递推出所有的(t)
这样就把所有的逆元(O(n))求出来了,我们再把原式变形:$$a_1{-1}k1+a_2{-1}k2+cdots+a_{n-1}{-1}k{n-1}+a_n{-1}kn$$
[k(a_1^{-1}+k(a_2^{-1}+cdots+k(a_{n-1}^{-1}+a_n^{-1}k)))
]
我们就可以(O(n))求出答案啦~~~
接下来是美滋滋的代码时间~~~(必须用快读。。)
#include<iostream>
#include<cstdio>
#include<cmath>
#define int long long
#define N 5000007
using namespace std;
int n,p,k;
int s[N],a[N],inv_s[N];
int Read()
{
int fu=1,ret=0;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')
fu=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
ret=(ret<<3)+(ret<<1)+c-'0';
c=getchar();
}
return ret*fu;
}
int qpow(int a,int b)
{
int ans=1,res=a;
while(b)
{
if(b&1)
ans=(ans*res)%p;
res=(res*res)%p;
b/=2;
}
return ans%p;
}
signed main()
{
n=Read(),p=Read(),k=Read();
s[0]=1;
for(int i=1;i<=n;++i)
{
a[i]=Read();
s[i]=(s[i-1]*a[i])%p;
}
inv_s[n]=qpow(s[n],p-2);
for(int i=n-1;i>=1;--i)
inv_s[i]=(inv_s[i+1]*a[i+1])%p;
int ans=0;
for(int i=n;i>=1;--i)
ans=((inv_s[i]*s[i-1])%p+ans)*k%p;
printf("%lld
",(ans+p)%p);
return 0;
}