我们有两个数a,b,要求a*b%p的结果。
如果a和b虽然都不超过long long,但乘在一起就超过了怎么办呢?
这里提供两种解决方案,适用于两种不同情况。
1、用龟速乘。
我们来想想,a*b的本质是什么?是b个a相加对吧?
相信大家都学过快速幂,快速幂求的是b个a相乘,那么我们灵活改一下,把它变成b个a相加,应该很简单吧?
这样由于是一点一点加上去的,每次加都取模,所以就不会爆long long了。
这就是龟速乘,时间复杂度同快速幂一样,是log(b)的。
模板:
long long cheng(long long a,long long b)
{
long long s=0;
while (b)
{
if (b&1) s=(s+a)%Mo;
a=(a+a)%Mo;
b>>=1;
}
return s;
}
2、用一种我并不知道名字的黑科技
我们先来分析一下上面这种做法的优劣:
好处是只要a,b在long long内,无论它们乘起来多大,都可以做。
劣势是时间复杂度是log(b)的,比起正常乘法来太慢了。
那我们有没有时间复杂度是O(1)的呢?
当然有,下文便是。
不过接下来提供的做法只适用于a*b没有超过long long太多的情况(即a,b并不算大,大概均在10^12左右)
设一个常数t。
令x1=a/t,x2=a%t
y1=b/t,y2=b%t
显然a*b=(x1*t+x2)*(y1*t+y2)=x1*y1*t*t+x1*y2*t+x2*y1*t+x2*y2。
我们要让这中间两个数乘起来均不超过long long即可。
具体t的取值参照a,b的大小,可自行考虑。(例如a,b均小于10^12时,t=10^6为宜)
这样,就可以实现边乘边模始终不爆啦。
O(1)实现!
补充一下,这种做法的本质其实是把龟速乘的转二进制改成了转10^6进制