乘法逆元

若线性同余方程 \(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;
}

P5431 【模板】乘法逆元 2

\(\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;
}
posted @ 2022-11-19 21:43  半步蒟蒻  阅读(43)  评论(0)    收藏  举报