学习博客
(博主讲的巨好
在做乘法运算的有时候会爆 l o n g long long l o n g long long,例如快速幂,这时候就可以用到快速乘。
一、复杂度为log的快速乘
我们知道乘法其实就是把很多个加法运算合到一起。现在我们的乘法会爆范围,那我们就把它转化为加法。一个一个加显然是不可能的,我们采用模拟二进制加法。
inline ll ksc(ll x, ll y, ll p){
ll res = 0;// 和
while(y){
if(y & 1) res = (res + x) % p;
x = (x << 1) % p; y>>=1;
}return res;
}
二、stl __int128
stl模板库里的好东西,复杂度接近 O ( 1 ) O(1) O(1),不过它需要手写输出,但是一般不用输出,只需要在运算的时候使用一下
long long ans=((__int128)x*y)%p
三、非常优秀的O(1)的快速乘
inline ll ksc(ll x,ll y,ll p){
ll z=(ld)x/p*y;
ll res=(ull)x*y-(ull)z*p;
return (res+p)%p;
}
// ll 表示 long long
// ld 表示 long double
// ull 表示 unsigned long long
// 一种自动溢出的数据类型(存满了就会自动变为0)
它其实很巧妙的运用了自动溢出这个操作,代码中的
z
z
z 表示
⌊
x
×
y
/
p
⌋
\lfloor x \times y /p\rfloor
⌊x×y/p⌋
x
×
y
−
⌊
x
×
y
/
p
⌋
×
p
x\times y - \lfloor x \times y /p\rfloor \times p
x×y−⌊x×y/p⌋×p
虽然这两个部分都是会溢出的,但(unsigned)保证了它们溢出后的差值基本不变,所以即使它会溢出也不会影响最终结果的!
四、 关于快速乘的灵活转化:
我们知道快速乘的原理其实就是乘法转加法(上面这种不算),但是这是可以根据题目性质灵活转变的,我们如何转成加法决定了我们的复杂度,就像如果模数并没有超过int范围很多,那我们适当的运用乘法分配律可以让复杂度非常接近 O ( 1 ) O(1) O(1)
inline ll ksc(ll x, ll y, ll P){
ll L=x*(y>>25)%P*(1<<25)%P;
ll R=x*(y&((1<<25)-1))%P;
return (L+R)%P;
}
在保证运算不会爆long long的前提下,我们可以尽量优化其复杂度,就像上述代码在模数小于 1 0 12 10^{12} 1012 的情况下完全变成了 O ( 1 ) O(1) O(1) 级别,在某些题目中会十分优秀!
本文介绍了在处理大整数乘法时可能会遇到的问题,如长整数溢出,并提出了解决方案,包括复杂度为log的快速乘、STL中的__int128类型以及一个巧妙利用溢出的O(1)快速乘方法。此外,还讨论了如何根据题目特性灵活转换算法以优化复杂度。示例代码展示了如何在模数较小的情况下实现接近O(1)复杂度的运算。
1663

被折叠的 条评论
为什么被折叠?



