同余方程、乘法逆元

题目链接

P1082 [NOIP2012 提高组] 同余方程

P2613 【模板】有理数取余


P1082 [NOIP2012 提高组] 同余方程

题目描述

求关于x的同余方程 \(a x \equiv 1 \pmod {b}\) 的最小正整数解。

输入格式

一行,包含两个正整数 \(a,b\),用一个空格隔开。

输出格式

一个正整数 \(x_0\) ,即最小正整数解。输入数据保证一定有解。

输入

3 10

输出

7

说明/提示

【数据范围】

对于 \(40\%\)的数据,\(2 ≤b≤ 1,000\)

对于 \(60\%\)的数据,\(2 ≤b≤ 50,000,000\)

对于 \(100\%\)的数据,\(2 ≤a, b≤ 2,000,000,000\)

解题思路

线性同余方程:给定整数 \(a,b,m,\) 求一个整数 \(x\) 满足 \(a \times x \equiv b(\mod m)\),或者给出无解。

\[a \times x \equiv b(\mod m) \Longleftrightarrow a \times x-b \, 是 \, m \, 的倍数,设为-y倍,即:a \times x +m\times y=b \]

此时将问题转化成了扩展欧几里得算法
有解时,求出一组整数 \(x_0,y_0\),满足 \(a\times x_0 +m \times y_0 =gcd(a,m)\),则通解:

\[x=x_0\times \frac{b}{gcd(a,m)}+k\times \frac{m}{gcd(a,m)} \]

代码

  • 时间复杂度:\(O(log(a+b))\)
#include<bits/stdc++.h>
using namespace std;
using LL=long long;
LL a,b;
LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    LL d=exgcd(b,a%b,x,y);
    LL z=x;
    x=y,y=z-y*(a/b);
    return d;
}
int main()
{
    scanf("%lld%lld",&a,&b);
    LL x,y;
    exgcd(a,b,x,y);
    printf("%lld",(x+b)%b);
    return 0;
}

P2613 【模板】有理数取余

题目描述

给出一个有理数 \(c=\frac{a}{b}\) ,求 \(c \bmod 19260817\) 的值。

输入格式

一共两行。

第一行,一个整数 \(a\)
第二行,一个整数 \(b\)

输出格式

一个整数,代表求余后的结果。如果无解,输出 Angry!

输入

233
666

输出

18595654

说明/提示

对于所有数据,保证 \(0\leq a \leq 10^{10001} ,1 \leq b \leq 10^{10001}\) ,且 \(a, b\) 不同时是 \(19260817\) 的倍数。

解题思路

同余方程

求解 \(\frac{a}{b} \bmod p\) 等价于求解 \(x \equiv \frac{a}{b} (\bmod p)\)\(x \bmod p\),而:

\[x \equiv \frac{a}{b} (\bmod p) \Longleftrightarrow bx\equiv a (\bmod p) \]

即求解同余方程~
至于大数,将大整数根据秦九韶公式写成“自左向右”,取模即可~

  • 时间复杂度:\(O(log(19260817 +b))\)

乘法逆元

显然,19260817 是质数,与 \(b\) 互质时(即 \(b \neq 0\) )可直接通过乘法逆元求解

  • 时间复杂度:\(O(log(19260815))\)

代码

  • 同余方程
#include<bits/stdc++.h>
using namespace std;
using LL=long long;
const LL mod=19260817;
string a,b;
LL A,B;
LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    LL d=exgcd(b,a%b,x,y);
    LL z=x;
    x=y,y=z-y*(a/b);
    return d;
}
int main()
{
    cin>>a>>b;
    for(int i=0;i<a.size();i++)
        A=(A*10+a[i]-'0')%mod;
    for(int i=0;i<b.size();i++)
        B=(B*10+b[i]-'0')%mod;
    LL x,y;
    LL d=exgcd(B,mod,x,y);
    if(A%d)puts("Angry!");
    else
        printf("%lld",(x*A/d%mod+mod)%mod);
    return 0;
}
  • 乘法逆元
#include<bits/stdc++.h>
using namespace std;
using LL=long long;
const LL mod=19260817;
string a,b;
LL A,B;
LL ksm(LL a,LL b)
{
    LL res=1%mod;
    for(;b;b>>=1)
    {
        if(b&1)res=res*a%mod;
        a=a*a%mod;
    }
    return res;
}
int main()
{
    cin>>a>>b;
    for(int i=0;i<a.size();i++)
        A=(A*10+a[i]-'0')%mod;
    for(int i=0;i<b.size();i++)
        B=(B*10+b[i]-'0')%mod;
    if(!B)puts("Angry!");
    else
        printf("%lld",(A*ksm(B,mod-2)+mod)%mod);
    return 0;
}

posted @ 2021-09-21 17:08  zyy2001  阅读(91)  评论(0编辑  收藏  举报