P5431 【模板】乘法逆元 2
Problem:
题目描述
给定 \(n\) 个正整数 \(a_i\) ,求它们在模 \(p\) 意义下的乘法逆元。
由于输出太多不好,所以将会给定常数 \(k\),你要输出的答案为:
\[\sum\limits_{i=1}^n\frac{k^i}{a_i}
\]
答案对 \(p\) 取模。
输入格式
第一行三个正整数 \(n,p,k\),意义如题目描述。
第二行 \(n\) 个正整数 \(a_i\),是你要求逆元的数。
输出格式
输出一行一个整数,表示答案。
样例 #1
样例输入 #1
6 233 42
1 4 2 8 5 7
样例输出 #1
91
提示
对于 \(30\%\) 的数据,\(1\le n \le 10^5\)。
对于 \(100\%\) 数据,\(1\le n \le 5\times 10^6\),\(2\le k < p \le 10^9\),\(1\le a_i < p\),保证 \(p\) 为质数。
Solution
注意到此题的 \(n\) 高达 5e6,因此我们无法求 O(n) 次逆元。
因此考虑通分,因为通分后就只剩一个大分数了,那么只用求分母的一次逆元即可。
很显然通分后是 \(\dfrac{\sum\limits_{i}k^i\prod\limits_{j\ne i}a_j}{\prod a_i}\)。
下面分母求一次逆元即可,上面分子的话我们不难发现 \(\prod\limits_{j\ne i}a_j=(\prod\limits_{j<i}a_j)(\prod\limits_{j>i}a_j)\),直接预处理前后缀积,然后 \(k^i\) 递推维护即可。
时间复杂度 \(O(n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define inf 0x7fffffff
#define timeused() (double)clock()/CLOCKS_PER_SEC
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define repp(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
#define mp make_pair
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
template<typename T> inline T rd(T& x){
T f=1;x=0;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(T)(c-'0');
x*=f;
return x;
}
ll n,p,k,a[6000005],pre[6000005],suf[6000005],ans;
ll qp(ll b,ll P,ll mod){
ll ans=1,base=b;
while(P){
if(P&1) ans=ans*base%mod;
base=base*base%mod;
P>>=1;
}
return ans;
}
int main(){
rd(n);
rd(p);
rd(k);
rep(i,1,n) rd(a[i]);
pre[0]=1;
suf[n+1]=1;
rep(i,1,n) pre[i]=pre[i-1]*a[i]%p;
repp(i,n,1) suf[i]=suf[i+1]*a[i]%p;
ll base=1;
rep(i,1,n){
base=base*k%p;
ans+=pre[i-1]*suf[i+1]%p*base%p;
ans%=p;
}
printf("%lld",ans*qp(pre[n],p-2,p)%p);
}