逆元

1 定义

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

2 费马小定理:

xab2(modb)p 为质数才成立

3 拓展欧几里得

void Exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) x = 1, y = 0;
    else Exgcd(b, a % b, y, x), y -= a / b * x;
}
int main() {
    ll x, y;
    Exgcd (a, p, x, y);
    x = (x % p + p) % p;
    printf ("%d\n", x); //x是a在mod p下的逆元
}

4 线性递推求法:

int inv[1000000];
void find_inv(int last,int p)
//求1~last所有数模p意义下的逆元
{
    inv[1]=1;//1的逆元就是1本身
    for(int i=2;i<=last;i++)
        inv[i]=(long long)(p-p/i)*(inv[p%i])%p;
    //注意longlong,否则有可能导致溢出
}

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

上面的方法只能求 1n 的逆元,如果需要求任意给定 n 个数(1ai<p)的逆元,就需要下面的方法:

首先计算 n 个数的前缀积,记为 si,然后使用快速幂或扩展欧几里得法计算 sn 的逆元,记为 svn

因为 svnn 个数的积的逆元,所以当我们把它乘上 an 时,就会和 an 的逆元抵消,于是就得到了 a1an1 的积逆元,记为 svn1

同理我们可以依次计算出所有的 svi,于是
ai1 就可以用 si1×svi 求得。

所以我们就在 O(n+logp) 的时间内计算出了 n 个数的逆元。

s[0] = 1;
for (int i = 1; i <= n; ++i) s[i] = s[i - 1] * a[i] % p;
sv[n] = qpow(s[n], p - 2);
// 当然这里也可以用 exgcd 来求逆元,视个人喜好而定.
for (int i = n; i >= 1; --i) sv[i - 1] = sv[i] * a[i] % p;
for (int i = 1; i <= n; ++i) inv[i] = sv[i] * s[i - 1] % p;

6 一些练习题

6.2 ../../ACMExercises/Luogu/B3645 数列前缀和 2 - 洛谷

链接