在一些题目中,因为数据量会特别大甚至超过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
如果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);
}
扩展欧几里得算法(英语: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;
}
当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;
}
求关于一个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;
}
除了以上几种,还有穷举、二进制扩展、牛顿迭代法等等
以下是关于以上方法的证明:
费马小定理更多
扩展欧几里得更多
递归更多
线性递推更多