同余方程、乘法逆元
题目链接
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)\),或者给出无解。
此时将问题转化成了扩展欧几里得算法
有解时,求出一组整数 \(x_0,y_0\),满足 \(a\times x_0 +m \times y_0 =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\),而:
即求解同余方程~
至于大数,将大整数根据秦九韶公式写成“自左向右”,取模即可~
- 时间复杂度:\(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;
}