问题:求
a
×
b
m
o
d
p
a\times b\ mod\ p
a×bmodp,
a
,
b
,
p
a,b,p
a,b,p 在 long long 范围内。
在 CRT 等算法中应用广泛。
为了处理模数在 int 范围外的情况,就是两数相乘可能会爆 long long 时,我们不能直接用整型的乘法来计算。
首先我们可以进行二进制拆分,化乘法为加法,类似快速幂那样,写出一个
O
(
log
n
)
O(\log n)
O(logn) 的快速乘
typedeflonglong s64;inlinevoidadd(s64 &a,const s64 &b){
a += b;if(a >= mod)
a -= mod;}inline s64 qmul(s64 a, s64 b,const s64 &mod){
a =(a % mod + mod)% mod;
b =(b % mod + mod)% mod;//这两行依据情况不写
s64 res =0;for(; b; b >>=1,add(a, a, mod))if(b &1)add(res, a, mod);return res;}
多次使用时,为了避免毒瘤出题人卡时间(或是为了优化常数),我们可以利用 long double 写出一个优秀的
O
(
1
)
O(1)
O(1) 快速乘。
简单原理:
a
×
b
m
o
d
p
=
a
×
b
−
⌊
a
×
b
p
⌋
×
p
a\times b\ mod\ p=a\times b-\lfloor \frac{a\times b}{p}\rfloor\times p
a×bmodp=a×b−⌊pa×b⌋×p
利用 long double 来处理这个
⌊
a
×
b
p
⌋
\lfloor \frac{a\times b}{p}\rfloor
⌊pa×b⌋。
然后处理一下浮点误差就可以了。
模数较大时可能会出锅。
不过出锅概率很小
typedeflonglong s64;typedeflongdouble ld;inline s64 qmul(s64 a, s64 b, s64 mod){
a =(a % mod + mod)% mod;
b =(b % mod + mod)% mod;//这两行依据情况不写
s64 res = a * b -(s64)((ld)a / mod * b +1e-8)* mod;return res <0? res + mod : res;}