GDUT 2020寒假训练 数论 C
原题链接
题目
Problem Description
要求(A/B)%9973,但由于A很大,我们只给出n(n=A%9973)(我们给定的A必能被B整除,且gcd(B,9973) = 1)。
Input
数据的第一行是一个T,表示有T组数据。
每组数据有两个数n(0 <= n < 9973)和B(1 <= B <= 10^9)。
Output
对应每组数据输出(A/B)%9973。
样例
input
2
1000 53
87 123456789
output
7922
6060
思路
扩展欧几里得求乘法逆元
对于
(
a
/
b
)
m
o
d
p
(a/b) mod p
(a/b)modp,设
∃
x
{\exist} x
∃x 满足
(
a
×
x
)
m
o
d
p
=
(
a
/
b
)
m
o
d
p
(a{\times}x)\ mod\ p=(a/b)\ mod\ p
(a×x) mod p=(a/b) mod p那么x也就是b在mod p意义下的倒数,也就是我们要找的b的逆元,即
x
≡
1
b
(
m
o
d
p
)
x {\equiv}{\frac{1}{b}}(mod\ p)
x≡b1(mod p)
那么将这个同余方程展开可得
x
+
p
y
=
1
b
x\ +\ py\ =\ {\frac{1}{b}}
x + py = b1
即
b
x
+
p
y
=
1
bx\ +\ py\ =\ 1
bx + py = 1这个形式就和扩展欧几里得的形式相似了,那么注意到p为质数也就是bp互质即
g
c
d
(
b
,
p
)
=
1
gcd(b,p)\ =\ 1
gcd(b,p) = 1那么现在这个形式就和拓展欧几里得的表达式一样了,利用拓展欧几里得计算exgcd(b,p,x,y,)最后的x就是b的逆元
代码
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long exgcd(long long a,long long b,long long &x,long long &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
long long d=exgcd(b,a%b,x,y);
long long temp=x;
x=y;
y=temp-(a/b)*y;
return d;
}
int main()
{
int T;
cin>>T;
while(T--)
{
long long n,b,x,y;
cin>>n>>b;
exgcd(b,9973,x,y);//x为b的逆元
x+=9973;
x%=9973;
cout<<(n*x)%9973<<endl;
}
return 0;
}
快速幂求乘法逆元
由费马小定理
假如a是一个整数,p是一个质数,那么 a p − p a^p\ -\ p ap − p 是p的倍数,表示为 a p ≡ a ( m o d p ) a^p\ {\equiv}\ a\ (mod\ p) ap ≡ a (mod p)
将表达式转换一下可以得到
a
p
−
1
≡
1
(
m
o
d
p
)
a^{p-1}\ {\equiv}\ 1\ (mod\ p)
ap−1 ≡ 1 (mod p)
即
a
×
a
p
−
2
≡
1
(
m
o
d
p
)
a{\times}a^{p-2}\ {\equiv}\ 1\ (mod\ p)
a×ap−2 ≡ 1 (mod p)
也就是
a
p
−
2
a^{p-2}
ap−2是
a
a
a的逆元,那么就可以通过快速幂求a的逆元了。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long power(long long a,long long b,long long mod)
{
long long ans=1%mod;
for(;b;b>>=1)
{
if(b&1)
{
ans=ans*a%mod;
}
a=a*a%mod;
}
return ans;
}
int main()
{
int T;
cin>>T;
while(T--)
{
long long n,b,x;
cin>>n>>b;
x=power(b,9973-2,9973);
//x+=9973;
//x%=9973;
cout<<(n*x)%9973<<endl;
}
return 0;
}