Loading

离线线性求逆元

这里还有一个方法可以在 \(O(n+\log P)\) 的时间复杂度内离线求出一个序列每个位置上的数的逆元是多少。这里该序列没有任何限制,可能唯一的限制就是逆元存在。

假设模数为 \(P\) 是一个质数。

考虑设该序列为 \(a_i\),设其前缀积为 \(Pre_i\),设其前缀积的逆元为 \(InvPre_i\),设该序列的逆元为 \(Inv_i\),我们来考虑怎么求。

我们先求出 \(Pre_i\),然后用 \(O(\log P)\) 的时间复杂度内求出 \(InvPre_n\),我们有以下式子成立:

\[ InvPre_n\times a_n\equiv InvPre_{n-1}\times a_n\times a_n^{-1}\equiv InvPre_{n-1}\bmod P \]

所以我们仍然可以线性求前缀积的逆元,然而每个位置的逆元满足:

\[ Inv_i=Pre_{i-1}\times InvPre_i \]

这里我们认为 \(Pre_0=1\)

所以我们就可以在上述复杂度内求出某个序列的逆元来了。


#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define mset(a,b) memset(a,b,sizeof(a))
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=r;i>=l;i--)
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define N 5000100
#define M number
using namespace std;

const int INF=0x3f3f3f3f;
const int mod=1e9+7;
const int base=998244353;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int n,a[N],Pre[N],InvPre[N],Inv[N];

inline int ksm(int a,int b,int mod){int res=1;while(b){if(b&1)res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}return res;}

int main(){
    read(n);rep(i,1,n)read(a[i]);
    Pre[0]=1;rep(i,1,n) Pre[i]=1ll*Pre[i-1]*a[i]%mod;
    InvPre[n]=ksm(Pre[n],mod-2,mod);
    dec(i,1,n-1) InvPre[i]=1ll*InvPre[i+1]*a[i+1]%mod;
    rep(i,1,n) Inv[i]=1ll*InvPre[i]*Pre[i-1]%mod;
    int now=1,ans=0;
    dec(i,1,n){ans=(ans+1ll*now*Inv[i]%mod)%mod;now=1ll*now*base%mod;}
    // rep(i,1,n) printf("%d ",Inv[i]);puts("");
    rep(i,1,n){assert(1ll*a[i]*Inv[i]%mod==1);}
    printf("%d\n",ans);
    return 0;
}

posted @ 2022-03-01 16:44  hyl天梦  阅读(53)  评论(0编辑  收藏  举报