「学习笔记」逆元

定义#

如果一个线性同余方程 ax1(modb),则 x 称为 amodb 的逆元,记作 a1

如何求逆元#

利用费马小定理#

前置知识:费马小定理。

「学习笔记」欧拉定理和费马小定理

如果 b 是质数,那么 ab11(modb),即 a×ab21(modb),逆元 x=ab2

我们可以用快速幂来实现。

快速幂代码:

ll qpow(ll x, ll y, ll mod) {
    ll res = 1;
    while (y) {
        if (y & 1) {
            res = res * x % mod;
        }
        y >>= 1;
        x = x * x % mod;
    }
    return res % mod;
}

利用扩展欧几里得算法#

前置知识:扩展欧几里得算法。

「学习笔记」扩展欧几里得定理与线性同余方程

利用扩展欧几里得算法求解,就是解不定方程 ax1(modb)

ax1(modb)ax+by=1

接下来按照扩展欧几里得算法解不定方程来做就行了。

ax+byc 的有解的要求是 cgcd(a,b) 的倍数,因此欧几里得算法求逆元的要求是 ab

扩展欧几里得算法的代码如下:

int exgcd(int a, int b, int& x, int& y) {
    if (!b) {
        x = 1, y = 0;
        return a;
    }
    int g = exgcd(b, a % b, y, x);
    y -= (a / b) * x;
    return g;
}

线性求逆元#

1 的逆元为 1,这是我们推算其他数的逆元的基础。

对于一个模数 p,我们设 k=pi,j=pmodi,则 p=ki+j,在 (modp) 意义下,ki+j0(modp),移项得 jki,同余式两边同乘 j1i1i1kj1,将 k=pi,j=pmodii1pi(pmodi)1,我们可以用 ppi 来代替 pi 这个负数。

最后的递推公式:i1(ppi)(pmodi)1(modp)

代码:

#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))

const int N = 3e6 + 5;

int n, p;
ll inv[N];

int main() {
    n = read<int>(), p = read<int>();
    inv[1] = 1;
    rep (i, 2, n, 1) {
        inv[i] = 1ll * (p - (p / i)) * inv[p % i] % p;
    }
    rep (i, 1, n, 1) {
        cout << inv[i] << '\n';
    }
    return 0;
}

线性求任意 n 个数的逆元#

给定任意 n 个数,要求线性时间复杂度求出这 n 个数的逆元。

先预处理出前 n 个数的前缀积 pro,随后处理出 pron 的逆元 invn,即 invn=(s1s2s3sn)1si 的逆元 si1=invnproi1j=i+1nsj,这样就能线性求出任意 n 个数的逆元了。

#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))

rep (i, 1, n, 1) {
    s[i] = read<ll>();
}
pro[0] = 1;
rep (i, 1, n, 1) {
    pro[i] = pro[i - 1] * s[i] % mod;
}
inv[n] = qpow(pro[n], mod - 2);
per (i, n, 1, 1) {
    inv[i - 1] = inv[i] * s[i] % mod;
}
rep (i, 2, n, 1) {
    inv[i] = inv[i] * pro[i - 1] % mod;
}

线性求阶乘的逆元#

阶乘计算公式:n!=i=1ni0!=1

与上面“线性求任意 n 个数的逆元”一样的道理,先预处理出阶乘 fac,随后我们算出 facn 的逆元 invn(invn=n!1),然后可以通过倒叙枚举来求出每个阶乘的逆元。

#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))

fac[0] = 1;
rep (i, 1, n, 1) {
    fac[i] = fac[i - 1] * i % mod;
}
inv[n] = qpow(fac[n], mod - 2);
per (i, n, 1, 1) {
    inv[i - 1] = inv[i] * i % mod;
}

作者:yifan0305

出处:https://www.cnblogs.com/yifan0305/p/17665436.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

转载时还请标明出处哟!

posted @   yi_fan0305  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示