首页 > 算法竞赛补充知识:乘法逆元学习
头像
2021UPC022
编辑于 2022-08-16 10:25
+ 关注

算法竞赛补充知识:乘法逆元学习

目录

1.乘法逆元定义

2.乘法逆元存在条件

3.求乘法逆元的方法

1.扩展欧几里德算法

2.费马小定理 + 快速幂

3.线性求1到n中每个数的逆元

4.求任意n个整数的乘法逆元


1.乘法逆元定义

逆元是指一个可以取消另一给定元素运算的元素。

比如,在实数的加法中,任意实数 a 和它的相反数 -a,互为加法逆元。因为对于任意实数 x,加上a之后,再加上-a,仍然等于 x,换言之,-a取消了x和a的加法运算。

再比如,在实数的乘法中,任意不为0的实数b和它的倒数 编辑,互为乘法逆元。因为对于任意实数x,乘上b之后,在乘上,仍然等于x,换言之, 取消了x 和 b 的乘法运算。

而在实际应用中,我们常常被要求对某一个质数取模,因此我们需要了解模意义下的乘法逆元运算。模运算下的乘法逆元定义:如果两个整数a 和 b 满足 ( a ∗ b − 1 )  mod  p = 0,即 a ∗ b ≡ 1 (  mod  p )。则称 a 和 b 互为模 p 的乘法逆元。

2.乘法逆元存在条件

a和p互质是a存在关于模p的乘法逆元b的充分必要条件。

3.求乘法逆元的方法

1.扩展欧几里德算法

算法具体作用:扩展欧几里得算法可以在求得a、b的最大公约数的同时,能找到整数x、y(其中一个很可能是负数),使它们满足裴蜀等式 ax + by = gcd(a,b)。

代码实现:

最终得到的x即为a在模b时候的乘法逆元(前提是return 的r等于1,即a,b互质)
int exgcd(int a,int b,int &x,int &y)
{
    if(b==0) {
        x=1;y=0;
        return a;
    }
    int r = exgcd(b,a%b,x,y);
    int t = y;
    y = x-(a/b)*y;
    x = t;
    return r;
}

2.费马小定理 + 快速幂

费马小定理:在所取得模p为质数的前提下,有,也就是有。所以a在模p的时候的乘法逆元为

代码实现:
inline int quick_power(int a, int b, int p) {
  int ans = 1;
  a = (a % p + p) % p;
  for (; b; b >>= 1) {
    if (b & 1) ans = (a * ans) % p;
    a = (a * a) % p;
  }
  return ans;
}
// quick_power(a, p-2, p) 即为 a 的乘法逆元。

3.线性求1到n中每个数的逆元

现在要求1到n中每个数i在模p时的乘法逆元。特别的,i = 1时,其乘法逆元i-1为1。

代码实现:
inv[1] = 1;
for (int i = 2; i <= n; ++i) {
  inv[i] = (p-p/i)*inv[p%i]%p;
}  
思路分析:
当 i > 1 时,不妨先设:
则有:
而且,在递推过程中,当我们要求时,对于任意的都是已知的(因为 p mod i肯定小于 i),所以可根据通过常数次运算得到

4.求任意n个整数的乘法逆元


s[0] = 1;
for (int i = 1; i <= n; ++i) s[i] = s[i - 1] * a[i] % p;
sv[n] = quick_power(s[n], p - 2);
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;

4.补充练习题

        题目:智乃酱的区间乘积
        补充知识:乘法逆元

        思路:前缀和思想,模版类型的前缀和是用于处理多次查询区间[l,r]的数组的元素的和,而前缀和的思想可以在理解之后进行进一步的应用,前缀和的思想的核心为:可以记录某一种操作,并且有对应的逆向操作可以取消这种操作的影响。而在读完题目之后,我们很容易就会想到前缀和,但是问题在于本题是要取模,所以前缀和sum[r] / sum[l-1]就不再适合,而应该选择在取模运算的下的乘法逆元。

        代码:
#define fi first
#define se second
#define pb(a)  push_back(a)
#define Q 1000000
#define EXP  1.000000000000000
#define mod 1000000007
#define EPS 1E-12
#define m_p make_pair
#define ft first
#define PI 3.141****53589
#define sc second
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
inline __int128 read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
inline void write(__int128 x){
    if(x<0){
        putchar('-');
        x=-x;
    }
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
ll fastpow(ll a,ll b,ll p)
{
    ll res = 1;
    while (b > 0)
    {
        if(b & 1)
        {
            res = res * a % p;
        }
        a = a * a % p;;
        b >>= 1;
    }
    res %= p;
    return res;
}
int get_inv(int x)
{
    return fastpow(x,mod - 2,mod);乘法逆元
}
ll a[100010];
ll sum[100010];
int main()
{
    // std::ios::sync_with_stdio(false);
    int N,M;
    cin>>N>>M;
    for (int i = 1; i <= N; i++)
    {
        a[i] = read();
    }
    sum[0] = 1;
    for (int i = 1; i <= N; i++)
    {
        sum[i] = sum[i-1] * a[i] % mod;
    }
    int l,r;
    while (M--)
    {
        scanf("%d%d",&l,&r);
        ll ans = sum[r] * get_inv(sum[l-1]) % mod;
        printf("%lld\n",ans);
    }
    return 0;
}



全部评论

(1) 回帖
加载中...
话题 回帖