在乘法計算 的結果時, (即unsigned long long範圍) ,如果直接算,顯然會溢出,這時,就可以用上快速乘法了 .
普通版
類似於快速冪,原理與快速冪類似,複雜度爲 .
原理:
設
則
當 爲1時, 纔會乘上 ,結果 加上 , .
LL fast_mul(LL a, LL b, LL p)
{
LL ans = 0;
a %= p;
while (b)
{
if (b&1)
ans = (ans+a) % p;
b >>= 1;
a <<= 1;
a %= p;
}
return ans;
}
dls版
先說一句dlsnb.
可以使用long double進行運算,時間複雜度爲 ,不過有時會使用不了,出現CE .
大佬解釋long double的輸出情況. 也可以直接看下面copy的結論 .
MSVC下long double爲8字節,而GCC編譯後的程序不論在Linux下還是在Windows下都爲12字節。不過仔細看可以發現,雖然GCC生成的程序佔用了12字節,但其只用到了前10字節(後2字節不論怎樣賦值其內容都不會發生改變),也就是說GCC的long double實際上是10字節(80bit)的。
除此之外,還可以發現,當使用scanf("%lf")時,不論變量是什麼類型的,都是按8字節存儲的(即按double類型存儲的),而使用scanf("%Lf"),則是按10字節存儲的(即按long double類型存儲的)。由於在MSVC下double = long double,所以不論怎麼混用,結果都是正確的。而在Linux下,我們發現,當存儲的long double爲真正的long double時(使用scanf("%Lf")),只能使用%Lf輸出結果,而long double內存儲的內容爲double時,只能使用輸出double的格式化字符串輸出。
所以猜想在GCC MinGW下,可能就像在Linux下存儲的double而強制輸出long double那樣會輸出爲0一樣,存儲的內容爲double,而MSVC將其認定爲long double輸出,所以最終結果爲0。
LL fast_mul(LL a, LL b, LL p)
{
if (p <= 1e9)
return a*b % p;
if (p <= 1000000000000LL)
return (((a*(b>>20)%p) << 20) + (a*(b&((1<<20)-1)))) % p;
LL d = (LL)floor(a * (long double)b/p+0.5);
LL res = (a*b - d*p) % p;
if (res < 0)
res += p;
return res;
}
__int128版
這時,你只需要在你的宏定義里加上下面一句 :
#define LL __int128
不過,由於__int128不在C/C++的IO中定義,只能進行運算,而不能進行輸入輸出 .