【费马小定理 / exgcd(求逆元)】洛谷 P2613 【模板】有理数取余 ——(分数取模)

本文详细解析了逆元的概念及其在大数运算中的应用,包括乘法逆元的定义,以及如何利用费马小定理和扩展欧几里德算法高效求解逆元。同时,介绍了大数取模的两种常见方法,提供了实际的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前话——何为逆元?

乘法逆元:

  • 每个 a 有且仅有一个 x ,使得ax Ξ 1 (mod p),且gcd(a, p) = 1,x称为a的逆元

或者说:

  • ax Ξ 1 (mod p):x是a的逆元
  • 一个数a有逆元的充分必要条件:gcd(a, m) = 1,逆元唯一存在

洛谷 P2613 【模板】有理数取余

  • 思路:

一:
a,b都是大数,所以我们怎么处理这两个数呢?这里就用到了两个取模的性质

(1) (a * b) % p = (a % p) * (b % p) % p
(2) (a ^ b) % p = ((a % p) ^ b) % p

具体:
一个大数来取模,我们可以想想除法的实现过程,是不是从高位到低位依次去除以除数,余数乘10加上下一位再继续除以除数,直到被除数的最后一位为止。
#####举个例子:1234 % 7
12 % 7 = 5 (商1)
53 % 7 = 4 (商7)
44 % 7 = 2 (商6)
————————1234 % 7 = 2

二:
除数b为0的时候是不存在答案的

费马小定理求逆元

证明

由a^(p-1) Ξ 1(mod p)
得a^(p-2) Ξ (1 / a)(mod p)(1 / a)(mod p) = a^(p-2) % p
于是得到:a^(p-2)为a的逆元

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define INF 0x3f3f3f3f
#define P(x) x>0 ? x : 0
#define MID l + r >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define lowbit(x) x & (-x)

using namespace std;
typedef long long ll;
typedef vector<int>:: iterator VITer;
const int maxN = 1e6 + 5;
const ll m = 19260817;

ll fast_power(ll x, ll y)
{
    ll base = x, ans = 1;
    while(y)
    {
        if(y & 1)
            ans = (ans % m) * (base % m) % m;
        base = (base % m) * (base % m) % m;
        y >>= 1;
    }
    return ans % m;
}

ll read(char *str)
{
    ll ans = 0;
    ll len = strlen(str);
    for(ll i = 0; i < len ; i ++)
        ans = (ans * 10 + (str[i] - '0') ) % m;
    return ans;
}

int main()
{
    char stra[maxN], strb[maxN];
    while(~ scanf("%s%s", stra, strb))
    {
        ll a = read(stra);
        ll b = read(strb);
        if(! b)
        {
            printf("Angry!\n");
            continue;
        }
        ll b_inverse = fast_power(b, m - 2);
        printf("%lld\n", a * b_inverse % m);
    }
    return 0;
}

扩展欧几里德求逆元

证明

给定质数p,设a的逆元为x
有ax Ξ 1 (% p)
即ax % p = 1
设ax = py + 1
则ax - py = 1 = gcd(a,p)
于是可以用扩展欧几里德求exgcd(a,p), 得到逆元x
注意为了防止负数出现,这样处理:x = (x % p + p) % p

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <list>
#include <map>
#define INF 0x3f3f3f3f
#define P(x) x>0 ? x : 0
#define MID l + r >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define lowbit(x) x & (-x)

using namespace std;
typedef long long ll;
typedef vector<int>:: iterator VITer;
const int maxN = 1e6 + 5;
const ll m = 19260817;

ll x, y;

ll exgcd(ll a, ll b)
{
    if( ! b ) { x = 1; y = 0; return a;}
    ll d = exgcd(b, a % b);
    ll tx = y;
    ll ty = x - (a / b) * y;
    x = tx;
    y = ty;
    return d;
}

ll read(char *str)
{
    ll ans = 0;
    ll len = strlen(str);
    for(ll i = 0; i < len ; i ++)
        ans = (ans * 10 + (str[i] - '0') ) % m;
    return ans;
}

int main()
{
    char stra[maxN], strb[maxN];
    while(~ scanf("%s%s", stra, strb))
    {
        ll a = read(stra);
        ll b = read(strb);
        if(! b)
        {
            printf("Angry!\n");
            continue;
        }
        ll d = exgcd(b, m);
        ll b_inverse = (x % m + m) % m;
        printf("%lld\n", a * b_inverse % m);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值