逆元原理详解



逆元的定义

逆元 (Inverse element) 就是在 mod 意义下,不能直接除以一个数,而要乘以它的逆元。

比如 a ∗ b ≡ 1 (mod p),那么 a,b 互为模 p 意义下的逆元,比如你要算 x / a % p,就可以改成 x * b % p 。


求逆元有很多方法,请看下面!


方法一:费马小定理 / 欧拉定理

费马小定理: 若 p 为素数,则有 a p − 1 ≡ 1   ( m o d   p ) a^{p−1}≡1\ (mod\ p) ap11 (mod p)
a p − 2   ∗   a ≡ 1   ( m o d   p ) a^{p−2}\ ∗\ a≡1\ (mod\ p) ap2  a1 (mod p)
a p − 2 a^{p−2} ap2 就是 a 在 mod p 意义下的逆元


欧拉定理: 若 a、p 互素,则有 a φ ( p ) ≡ 1 ( m o d   p ) a^{φ(p)}≡1(mod\ p) aφ(p)1(mod p) (费马小定理的一般形式)
a φ ( p ) − 1   ∗   a ≡ 1   ( m o d   p ) a^{φ(p)-1}\ ∗\ a≡1\ (mod\ p) aφ(p)1  a1 (mod p)
a φ ( p ) − 1 a^{φ(p)-1} aφ(p)1 就是 a 在 mod p意义下的逆元

性能分析: O ( l o g   m o d ) O(log\ mod) O(log mod)
适用范围:一般在 mod 是个素数的时候用,比扩展欧几里得方法快一点而且好写。

Code:

ll q_pow(ll a,ll b,ll mod){
    ll ans=1,res=a%mod;
    while(b){
        if(b&1)    ans=ans*res%mod;
        res=res*res%mod;
        b>>=1;
    }
    return ans%mod;
}
ll inv(ll a,ll mod){
    return q_pow(a,mod-2,mod)%mod;
}


经典例题

例题1:http://poj.org/problem?id=1845

题意: 给定两个正整数 A 和 B ,求 A B A^B AB 的所有因子和对9901取余后的值。



例题2: https://www.lydsy.com/JudgeOnline/problem.php?id=2186

题意: 求 1 到 N! 中与 M! 互质的数的个数,其中 M <= N 。



例题3: http://codeforces.com/contest/964/problem/C

题意:



例题4: https://ac.nowcoder.com/acm/contest/3005/C

思路: 这里用尺取法 + 逆元来做。l 代表左端点,r 代表右端点。l 先不动,r 往前扫描,如果成功扫到,有 k 个非0元素的子段就累乘起来,最后把最左端的元素除了(用乘法逆元,否则会出现除以 0 的异常),左端点往前移动,l++,再继续扫描。在未达到 k 个非零元素的子段前,如果遇到 0,当前的区间重置 ,左端点直接到 0 的下一个位置继续扫描。

#include <iostream>
using namespace std;
typedef long long ll;
const int N=2e5+100;
const ll mod=998244353;
int n,k;
ll a[N];
ll q_pow(ll a,ll b){
    ll ans=1,res=a%mod;
    while(b){
        if(b&1)    ans=ans*res%mod;
        res=res*res%mod;
        b>>=1;
    }
    return ans%mod;
}
ll inv(ll a,ll mod){
    return q_pow(a,mod-2)%mod;
}
int main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    int l=1,r=1;
    ll mul=1,ans=0;
    while(r<=n){
        if(a[r]){
            mul=mul*a[r]%mod;
            if(r-l+1==k){
                ans=max(ans,mul);
                mul=mul*inv(a[l],mod)%mod;
                l++;
            }
        }
        else{
            mul=1;
            l=r+1;
        }
        r++;
    }
    cout<<ans<<endl;
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值