乘法逆元的线性筛法

目录

目录地址

上一篇

下一篇


线性求逆元题型

对于已知模数 \(m\) ,求出在模 \(m\) 意义下, \(1\)~\(n\) 的逆元 ( \(n\leq m-1\)

\(n\) 较大,只支持 \(O(n)\) 复杂度的算法

(一般保证 \(m\) 是质数,否则有的数不存在逆元)


线性算法 \(O(n)\)

由递推的方法 \(O(n)\)

考虑模 \(m\) 意义下 \(1^{-1}\equiv 1(\mod m)\)

考虑求 \(n\) 的逆元,可知 \(m=\lfloor{m\over n}\rfloor\cdot n+(m\mod n)\)

\(a=m/n,b=m\%n\)\(m=an+b,0\leq b<n\)

因此 \(an+b\equiv 0(\mod m)\)

方程两边乘上 \(n^{-1}\) 得到

\(a+b\cdot n^{-1}\equiv 0(\mod m)\)

\(n^{-1}\equiv -a\cdot b^{-1}(\mod m)\)

\(n^{-1}\equiv -\lfloor{m\over n}\rfloor\cdot (m\mod n)^{-1}(\mod m)\)

inv[1]=1;
for(int i=2;i<=n;i++){
    inv[i]=m-m/n*inv[m%n]%m;
}

线性筛法 \(O(n)\)

利用线性筛的性质,以及逆元在取模意义下的积性,可以很快写出线性筛的方法:

inv[1]=1;
for(int i=2;i<=n;i++){
    if(fc[i]==0){
        fc[i]=i;
        prime[cntprime++]=i;
        inv[i]=fpow(i,m-2);
    }
    for(int j=0;j<cntprime&&prime[j]<=fc[i];j++){
        inv[i*prime[j] ]=inv[i]*inv[ prime[j] ];
    }
}

因为质数的个数大约是 \(n\over \ln n\) 个,快速幂的复杂度 \(O(\log m)\)\(n\leq m-1\) ,因此质数部分的复杂度为 \(O(n)\)

而合数的个数大约是 \((n-{n\over \ln n})\) 它们是线性的,因此复杂度为 \(O(n)\)

最终得出总复杂度为 \(O(n)\)


阶乘法 \(O(n)\)

考虑到我们可以 \(O(n)\) 内求出 \(\forall i\leq n,i!\)

而我们可以花费 \(O(\log m)\) 的时间求出 \(n!^{-1}\) ,由于 \(n\leq m-1\) ,复杂度也可以认为是 \(O(n)\)

再利用公式: \(i!^{-1}\equiv (i+1)!^{-1}\cdot (i+1)(\mod m),i^{-1}\equiv i!^{-1}\cdot (i-1)!(\mod m)\)

得出结论:

fac[0]=1;
for(int i=1;i<=n;i++){
    fac[i]=fac[i-1]*i%m;
}
invf[n]=fpow(fac[n],m-2);
for(int i=n-1;i>=0;i--){
    invf[i]=invf[i+1]*(i+1)%m;
}
for(int i=1;i<=n;i++){
    inv[i]=fac[i]*invf[i-1]%m;
}

总复杂度 \(O(n)\)

posted @ 2020-03-03 10:02  JustinRochester  阅读(548)  评论(0编辑  收藏  举报