乘法逆元
若线性同余方程 \(ax\equiv1(\mod b)\),则称 \(x\) 为 \(a\mod b\)时的逆元,记作\(a^{-1}\)。
扩展欧几里得求逆元。
要求 \(gcd(a,b)=1\).
int exgcd(int a,int b,int&x,int&y){
if(!b)return x=1,y=0,a;
int g=exgcd(b,a%b,y,x);
y-=a/b*x;
return g;
}
\((x\mod p+p)\mod p\) 即为 \(a\mod b\) 逆元。
费马小定理求逆元。
要求 \(b\) 为质数。
\(ax\equiv a^{b-1}(\mod b)\),则 \(x\equiv a^{b-2}(\mod b)\).
inline int po(int x,int k,int mod,int re=1){for(;k;k>>=1,x=x*x%mod)(k&1)&&(re=re*x%mod);return re;}
\(po(a,b-2,b)\) 即为 \(a\mod b\) 的逆元。
线性求 \(1\) 到 \(n\) 的逆元。
\(1\) 的逆元为 \(1\).
设 \(k=\left \lfloor p/i \right \rfloor,j=p\mod i\),则 \(p=k*i+j\),\(k*i+j \equiv 0(\mod p)\).
两边同乘 \(i^{-1}+j^{-1}\),得到 \(k*j^{-1}+i^{-1} \equiv 0(\mod p)\).
于是有 \(i^{-1} \equiv -\left \lfloor p/i \right \rfloor * (p\mod i)^{-1}\),将负数加上模数即可。
inline void prework(int n){
inv[1]=1;
for(int i=2;i<=n;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
线性求任意 \(n\) 个数的逆元。
设所求序列为 \(a[]\),计算其前缀积 \(s[]\),求出 \(s_n\) 的逆元 \(sv_n\),将其乘 \(a_n\) 就会得到 \(a_{1...n-1}\) 的积逆元,\(a_i^{-1}=sv_i*s_{i-1}\)
inline void prework(){
s[0]=1;
for(int i=1;i<=n;i++)s[i]=s[i-1]*a[i]%mod;
sv[n]=po(s[n],mod-2,mod);
for(int i=n;i>=1;i--)sv[i-1]=sv[i]*a[i]%mod;
for(int i=1;i<=n;i++)inv[i]=sv[i]*s[i-1]%mod;
}
也可以只求一个整体的积的逆元,再求前缀积和后缀积
\(inv_i=sv*pre_{i-1}*suc_{i+1}\).
inline void prework(int n){
pre[0]=suc[n+1]=1;
for(int i=1;i<=n;i++)pre[i]=pre[i-1]*a[i]%mod;
sv=po(pre[n],mod-2,mod);
for(int i=n;i;i--)suc[i]=suc[i+1]*a[i]%mod;
}
线性求阶乘逆元。
\(\frac{1}{(n+1)!}*(n+1)=\frac{1}{n!}\),求出 \(facinv_n\) 之后倒推。
inline void prework(int n){
fac[0]=1;
for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
facinv[n]=po(fac[n],mod-2,mod);
for(int i=n;i;i--)facinv[i-1]=facinv[i]*i%mod;
}
求 \(\sum_{i=1}^{n}\frac{k^i}{a_i}\).
一堆分数相加,将其通分。
设 \(s=\prod_{i=1}^{n}a_i\),第 \(i\) 个分数变成了 \(\frac{k^i*(s/a_i)}{s}\).
将分子累加,最后再乘上分母的逆元即可。
inline void prework(int n){
pre[0]=suc[n+1]=1;
for(int i=1;i<=n;i++)pre[i]=pre[i-1]*a[i]%mod;
for(int i=n;i;i--)suc[i]=suc[i+1]*a[i]%mod;
}
signed main(){
cin>>n>>mod>>k;
for(int i=1;i<=n;i++)cin>>a[i];
prework(n);
int ans=0,tmp=k;
for(int i=1;i<=n;i++){
(ans+=pre[i-1]*suc[i+1]%mod*tmp%mod)%=mod;
(tmp*=k)%=mod;
}
cout<<ans*po(pre[n],mod-2,mod)%mod;
return 0;
}
也可以暴力求逆元之后直接算,求一个后缀积和整体的逆元,维护前缀积,即可得到单个数的逆元。
signed main(){
cin>>n>>mod>>k;
for(int i=1;i<=n;i++)cin>>a[i];
suc[n+1]=1;
for(int i=n;i;i--)suc[i]=suc[i+1]*a[i]%mod;
inv=po(suc[1],mod-2,mod);
int ans=0,tmp=k,pre=1;
for(int i=1;i<=n;i++){
(ans+=tmp*inv%mod*pre%mod*suc[i+1])%=mod;
(pre*=a[i])%=mod;
(tmp*=k)%=mod;
}
cout<<ans;
return 0;
}