洛谷 P5431 【模板】乘法逆元2 题解
假的模板题吧
题目传送门
题目大意:给定 \(n\) 个正整数 \(a_i\) 和 \(p,k\) ,求
\[\sum^n_{i-1}\frac{k^i}{a_i}
\]
答案对 \(p\) 取余数。
数据范围:
\(1\le n \le 5\times10^6\)
\(1 \le k < p \le 10^9\)
\(1\le a_i < p\)
题目解析
其实这是一道 小 学 题 目
首先我们通分一下,分母当然是小学生做法,直接取 \(\prod a_i\)
不难得出:
\[\sum^n_{i-1}\frac{k^i}{a_i}=\frac{ \sum^n_{i=1} \left( k^i \times \prod^{i-1}_{j=1}a_j \times \prod^n_{j=i+1}a_j \right) }{\prod a_i}
\]
显然预处理出数组 \(a\) 所有的前缀积和后缀积就好了。
记得除以 \(\prod a_i\) 的时候要用上逆元(不然怎么叫做逆元呢)。
复杂度是 \(O\left( 3n+\log_2p \right)\) 卡卡常数就可以了。
#include<cstdio>
#define maxn 5000039
using namespace std;
//#define debug
typedef long long ll;
typedef int Type;
inline Type read(){
Type sum=0;
int flag=0;
char c=getchar();
while((c<'0'||c>'9')&&c!='-') c=getchar();
if(c=='-') c=getchar(),flag=1;
while('0'<=c&&c<='9'){
sum=(sum<<1)+(sum<<3)+(c^48);
c=getchar();
}
if(flag) return -sum;
return sum;
}
int a[maxn],n;
ll head[maxn],tail[maxn],p,k;
ll getinv(ll x){
int y=p-2;
ll tmp=x,res=1;
while(y){
if(y&1) res=res*tmp%p;
y>>=1; tmp=tmp*tmp%p;
}
return res%p;
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
n=read(); p=read(); k=read();
for(int i=1;i<=n;i++)
a[i]=read();
head[0]=tail[n+1]=1;
for(int i=1;i<=n;i++) head[i]=head[i-1]*a[i]%p;
for(int i=n;i>=1;i--) tail[i]=tail[i+1]*a[i]%p;
ll res=0,tmp=k;
for(int i=1;i<=n;i++){
res=(res+tmp*head[i-1]%p*tail[i+1]%p)%p;
tmp=tmp*k%p;
}
printf("%lld",res*getinv(head[n])%p);
return 0;
}