Loading

线性求逆

推导过程(1~n)

\(1^{-1}\equiv 1(\bmod p)\)

\(p=k\times i+r,r<i,1<i<p\)

将这个式子放在 \(\bmod p\) 的意义下,得到 \(k\times i+r\equiv 0(\bmod p)\)

两边同时乘上 \(i^{-1},r^{-1}\),得到

\(k\times r^{-1}+i^{-1}\equiv 0(\bmod p)\)

\(i^{-1}\equiv-k\times r^{-1}\equiv0(\bmod p)\)

\(i^{-1}\equiv-\lfloor\frac{p}{i}\rfloor\times(p \bmod i)^{-1}(\bmod p)\)

\(p \bmod i^{-1}\)​ 是已经求过的了,因此 \(i^{-1}\)​ 可以线性求出。

ni[1]=ni[0]=1;
for (int i=2;i<=mx;++i)
	ni[i]=(ll)(mod-mod/i)*ni[mod%i]%mod;

推导过程(任意 n个)

求出 \(n\) 个数的前缀积,记作 \(s\)

用快速幂或者扩展欧几里得求出 \(s_n\) 的逆元,记作 \(sv_n\)

那么就有 \(sv_i=sv_{i+1}*a_{i+1}\),因为 \(a_{i+1}\) 会和 \(sv_{i+1}\)\(a_{i+1}^{-1}\) 抵消。

求出 \(sv\) 后,\(inv_i=sv_i*s_{i-1}\)

#include<cstdio>
#define N 5000005
#define mod 1000000007
#define ll long long
using namespace std;
int n;
ll ans,a[N],s[N],sv[N],inv[N];
ll ksm(ll x,ll y)
{
    ll res=1;
    while (y)
    {
        if (y&1) res=res*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return res;
}
ll read()
{
    ll res=0;int fh=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') fh=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') {res=(res<<1)+(res<<3)+(ch-'0');ch=getchar();}
    return res*fh;
}
int main()
{
    scanf("%d",&n);
    s[0]=1;
    for (int i=1;i<=n;++i)
        a[i]=read(),s[i]=s[i-1]*a[i]%mod;
    sv[n]=ksm(s[n],mod-2);
    for (int i=n-1;i;--i)
        sv[i]=sv[i+1]*a[i+1]%mod;
    for (int i=1;i<=n;++i)
        inv[i]=sv[i]*s[i-1]%mod;
    for (int i=1;i<=n;++i)
        ans=(ans+sv[i]*s[i-1]%mod*ksm(1ll*998244353,n-i)%mod)%mod;
    printf("%lld\n",ans);
    return 0;
}
posted @ 2021-11-06 15:44  Thunder_S  阅读(95)  评论(0编辑  收藏  举报