洛谷 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;
}
posted @ 2021-04-25 19:50  jiangtaizhe001  阅读(79)  评论(0编辑  收藏  举报