原因
在一些题目中,因为数据量会特别大甚至超过ll,所以会要求最后结果mod一个数,实际上就是让你在计算过程中就要不断mod
对于加法:
(
a
+
b
)
%
m
=
(
a
%
m
+
b
%
m
)
%
m
(a+b)\%m = (a\%m+b\%m)\%m
(a+b)%m=(a%m+b%m)%m
对于减法:
(
a
−
b
)
%
m
=
(
a
%
m
−
b
%
m
)
%
m
(a-b)\%m = (a\%m-b\%m)\%m
(a−b)%m=(a%m−b%m)%m
对于乘法:
(
a
∗
b
)
%
m
=
(
a
%
m
∗
b
%
m
)
%
m
(a*b)\%m = (a\%m*b\%m)\%m
(a∗b)%m=(a%m∗b%m)%m
但是这个规则在除法不适用:简单例子比如
(
30
7
)
%
2
(\frac{30}{7})\%2
(730)%2
为了对除法也能进行模运算,就需要乘法逆元
什么是乘法逆元
若c是b的逆元,则有
b
∗
c
≡
1
(
m
o
d
m
)
b*c≡1(mod m)
b∗c≡1(modm),称c为b关于m的乘法逆元
例如
b
=
10
,
m
=
3
b=10,m=3
b=10,m=3时
c
=
4
c=4
c=4
令
a
=
20
,
(
20
/
10
)
%
3
=
2
,
(
20
∗
4
)
%
3
=
2
a=20, (20/10)\%3=2 ,(20*4)\%3=2
a=20,(20/10)%3=2,(20∗4)%3=2
令
a
=
40
,
(
40
/
10
)
%
3
=
1
,
(
40
∗
4
)
%
3
=
1
a=40, (40/10)\%3=1 ,(40*4)\%3=1
a=40,(40/10)%3=1,(40∗4)%3=1
现在我们要求(a/b)%m,可以找到一个c使得(a/b)%m=(a*c)%m
求法
1.费马小定理
如果p是一个质数,而整数a不是p的倍数,则有a(p-1)≡1(mod p)
所以可得a(p-2) ≡ a-1(mod p)
换成代码如下
(a/b)%m
(a*pow(b,m-2))%m
这里还需要快速幂来配合计算
ll quick_pow(ll x,ll n,ll m)
{
ll res = 1;
while(n > 0)
{
if(n & 1)
res = res * x % m;
x = x * x % m;
n >>= 1;
}
return res;
}
ll inv(ll a)
{
return quick_pow(a,mod - 2,mod);
}
2.扩展欧几里得
扩展欧几里得算法(英语:Extended Euclidean algorithm)是欧几里得算法(又叫辗转相除法)的扩展。已知整数a、b,扩展欧几里得算法可以在求得a、b的最大公约数的同时,能找到整数x、y(其中一个很可能是负数),使它们满足贝祖等式ax+by=gcd(a,b)
贝祖定理:即如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。
如果a是负数,可以把问题转化成|a|(-x)+by=gcd(|a|,b),然后令x’=(-x)。
ll gcdEx(ll a,ll b,ll &x,int &y)
{
if(b==0)
{
x=1,y=0 ;
return a ;
}
else
{
int r=gcdEx(b,a%b,x,y);
/* r = GCD(a, b) = GCD(b, a%b) */
int t=x ;
x=y ;
y=t-a/b*y ;
return r ;
}
}
gcd(a,m)=1!!!
当y=0,逆元即ax+my=1 (mod m)中的x
利用欧几里得算法不断递归直到x=1,y=0—>反向递归求出第一层的x和y,x即为a模m的逆元。
ll inv(ll a,ll m)
{
ll x,y;
if(gcdEx(a,m,x,y)!=1)
return -1;//不互质 不存在逆元
return (x%m+m)%m;
}
3.递归
当m是质数
inv(a) = (m - m / a) * inv(m % a) % m
暴力反向递归
ll inv2(ll a,ll m)//代入a%m m
{
return a==1?1:(m-m/a)*inv2(m%a,m)%m;
}
4.线性递推
求关于一个m的多个逆元,道理差不多
ll m=3;
ll inv[m+5];
void inv3(ll m)
{
inv[1]=1;
for(int i=2;i<m;i++)
inv[i]=(m-m/i)*inv[m%i]%m;
}
除了以上几种,还有穷举、二进制扩展、牛顿迭代法等等
以下是关于以上方法的证明:
费马小定理更多
扩展欧几里得更多
递归更多
线性递推更多