Θ ( 1 ) Θ(1) Θ(1)模取乘
分析
我们把两个 1 0 18 10^{18} 1018 的数乘起来会发生什么?会炸掉?会循环, 9223372036854775807 + x 9223372036854775807+x 9223372036854775807+x 会变成 − 9223372036854775808 + x − 1 −9223372036854775808+x−1 −9223372036854775808+x−1 (因为 − 9223372036854775808 − 1 = 9923372036854775807 -9223372036854775808-1=9923372036854775807 −9223372036854775808−1=9923372036854775807 )。
怎么规避负数然后取模呢?
这样: − 9223372036854775808 + a b − 1 − ( − 9223372036854775808 + ⌊ a b c ⌋ × c − 1 ) = a b − ⌊ a b c ⌋ × c = a b m o d c −9223372036854775808+ab−1−(−9223372036854775808+⌊\frac{ab}{c}⌋×c−1)=ab−⌊\frac{ab}{c}⌋×c=ab\mod c −9223372036854775808+ab−1−(−9223372036854775808+⌊cab⌋×c−1)=ab−⌊cab⌋×c=abmodc 。
如果 l o n g d o u b l e long\space double long double 也救不了你的话就算了。
代码
typedef long long LL;
LL mul(LL a,LL b,LL c)
{
return ((a*b-(LL)((long double)a/c*b+1e-8)*c)%c+c)%c;
}
版权声明:本文为CSDN博主「C20182030Epic」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
备注
上面的说法好像并不完全正确?
因为 a b ab ab 可能“爆很多圈”,也就是说如果 a b = 1 0 36 ab=10^{36} ab=1036 的话……?
所以作者认为,原理应该是:
- 第一点, 0 ≤ A − B < C ⇒ ( A m o d C ) − ( B m o d C ) ∈ { A − B + k C ∣ k ∈ { 0 , 1 } } 0\le A-B<C\Rightarrow(A\bmod C)-(B\bmod C)\in\{A-B+kC|k\in\{0,1\}\} 0≤A−B<C⇒(AmodC)−(BmodC)∈{A−B+kC∣k∈{0,1}} 。
很简单的道理:
- 设 B = k C + m ( 0 ≤ m < C ) B=kC+m(0\le m<C) B=kC+m(0≤m<C)
- 则 A = k C + m + n ( 0 ≤ n < C ) A=kC+m+n(0\le n<C) A=kC+m+n(0≤n<C)
- 故 A m o d C ∈ { m + n , m + n − C } A\bmod C\in\{m+n,m+n-C\} AmodC∈{m+n,m+n−C}
- 而 B m o d C = m B\bmod C=m BmodC=m
所以得证。
-
第二点,“溢出”的本质是 x x x 变为 x m o d 2 64 x\bmod 2^{64} xmod264 。
-
第三点,无论如何, a b m o d c = a b − c ⌊ a b c ⌋ ab\bmod c=ab-c\lfloor\frac{ab}{c}\rfloor abmodc=ab−c⌊cab⌋ 。
将第三点中的 a b ab ab 当做 A A A , c ⌊ a b c ⌋ c\lfloor\frac{ab}{c}\rfloor c⌊cab⌋ 当做 B B B , 2 64 2^{64} 264 当做 C C C ,代入第一点,即可证明。