我們有兩個數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進制