学习笔记:快速幂、逆元
首先说一下取模的运算:
(
a
+
b
)
%
m
o
d
=
(
a
%
m
o
d
+
b
%
m
o
d
)
%
m
o
d
\left(a+b\right)\%mod=\left(a\%mod+b\%mod\right)\%mod
(a+b)%mod=(a%mod+b%mod)%mod
(
a
−
b
)
%
m
o
d
=
(
a
%
m
o
d
−
b
%
m
o
d
+
m
o
d
)
%
m
o
d
\left(a-b\right)\%mod=\left(a\%mod-b\%mod+mod\right)\%mod
(a−b)%mod=(a%mod−b%mod+mod)%mod
(
a
∗
b
)
%
m
o
d
=
(
a
%
m
o
d
∗
b
%
m
o
d
)
%
m
o
d
\left(a*b\right)\%mod=\left(a\%mod*b\%mod\right)\%mod
(a∗b)%mod=(a%mod∗b%mod)%mod
但是:
(
a
/
b
)
%
p
=
(
a
%
p
/
b
%
p
)
%
p
\left(a/b\right)\%p=\left(a\%p/b\%p\right)\%p
(a/b)%p=(a%p/b%p)%p 是错误的!!!等式不成立。
例如:
(
6
/
2
)
%
2
=
1
\left(6/2\right)\%2=1
(6/2)%2=1 ,
(
6
%
2
/
3
%
2
)
%
2
=
0
\left(6\%2/3\%2\right)\%2=0
(6%2/3%2)%2=0
切入正题:逆元
( a ∗ x ) % p = 1 \left(a∗x\right)\%p=1%mod (a∗x)%p=1, x x x 就等于 1 a \frac{1}{a} a1吗,不一定,因为这里还有条件呢: m o d mod mod p,所以在这个条件下 x x x叫做 a a a关于 p p p的逆元,比如: ( 2 ∗ 3 ) % 5 = 1 (2∗3)\%5=1 (2∗3)%5=1,那么 3 3 3关于 5 5 5的逆元就是 2 2 2或者说 2 2 2关于 5 5 5的逆元就是 3 3 3, 2 2 2和 3 3 3关于 5 5 5互为逆元。
怎么求逆元呢?
费马小定理:
a
p
−
1
%
p
=
1
a^{p-1}\%p=1
ap−1%p=1(
a
a
a与
p
p
p互质),
两边同时除
a
a
a得:
a
p
−
2
%
p
=
1
a
a^{p-2}\%p=\frac{1}{a}
ap−2%p=a1,大功告成,
a
a
a关于
p
p
p的逆元就是
a
p
−
2
%
p
a^{p-2}\%p
ap−2%p,记为
i
n
v
(
a
)
=
a
p
−
2
%
p
inv(a)=a^{p-2}\%p
inv(a)=ap−2%p.
再回到刚开始的问题:如何求 ( a / b ) % p (a/b)\%p (a/b)%p,应用刚才的费马小定理即转换为 ( a ∗ i n v ( b ) ) % p (a*inv(b))\%p (a∗inv(b))%p再转化为 ( a % p ∗ i n v ( b ) % p ) % p (a\%p*inv(b)\%p)\%p (a%p∗inv(b)%p)%p,就完全转换成好算的乘法了,所以就求出 i n v ( b ) = b p − 2 % p inv(b)=b^{p-2}\%p inv(b)=bp−2%p就可以算出结果了。
b
p
−
2
b^{p-2}
bp−2就可以用快速幂解决,复杂度
O
(
l
o
g
n
)
O(logn)
O(logn)
下面代码求
a
b
%
m
o
d
a^b\%mod
ab%mod
long long POW(long long a,long long b,long long mod)
{
long long base = a,ans=1;
while(b)
{
if(b&1)
ans=(ans*base)%mod;
base=(base*base)%mod;
b>>=1;
}
return ans;
}
再来个例题:hdu 1576 A/B
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。
Sample Input
2
1000 53
87 123456789
Sample Output
7922
6060
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<math.h>
using namespace std;
const int maxn=30010;
long long POW(long long a,long long b,long long mod)
{
long long base = a,ans=1;
while(b)
{
if(b&1)
ans=(ans*base)%mod;
base=(base*base)%mod;
b>>=1;
}
return ans;
}
int main()
{
int t;
cin>>t;
while(t--)
{
long long n,b;
cin>>n>>b;//n=A%mod
long long inv_b=POW(b,9973-2,9973);
long long ans=(n*inv_b)%9973;
printf("%lld\n",ans);
}
return 0;
}